feat(loader): integrate bounded rank-local FastSafeTensors copyout - #1354
feat(loader): integrate bounded rank-local FastSafeTensors copyout#1354ABNER-1 wants to merge 10 commits into
Conversation
LLLLKKKK
left a comment
There was a problem hiding this comment.
AI Code Review - PR #1354
Status: BLOCKING
Summary: P0/0 · P1/3 · P2/11 · P3/5
Reviewed: commit dcbd9388ab1d · 2026-08-30 23:30 UTC+8
Blocking Issues
P1
- AutoLoader 硬依赖缺版本闸门,cuda12_9 pin 未同步且无能力探测与回退 @
rtp_llm/utils/database.py:316- 建议:二者择一并明确表态:(1)在本 PR 内把 cuda12_9 及其它仍在
0.1.19的平台 requirements/lock 升到提供AutoLoader+local_copyout_filter的版本,并在fastsafetensors_weights_iterator入口保留一次性能力断言(inspect.signature校验形参),不匹配时 fail-fast 并给出「所需最低版本 + 当前版本」;(2)若要保留运维回滚路径,把 AUTO 选路的has_module换成能力探测(仓内已有module_util.resolve_symbol),失败时 warning 并回落LoadMethod.SCRATCH。同时统一策略:_fastsafetensors_transient_budget_bytes对旧 wheel 做优雅降级、导入侧却硬失败,两处自相矛盾;并在docs/backend/server_arguments.md补最低版本、生效平台、失败现象(ImportError: cannot import name 'AutoLoader')与--load_method scratch回退指引,替代被删闸门原先承担的可读提示职责。
- 建议:二者择一并明确表态:(1)在本 PR 内把 cuda12_9 及其它仍在
- 删除逐 expert 广播后 stacked MoE 多 rank 峰值显存回退且无实测 @
rtp_llm/utils/database.py:363- 建议:给出同一 stacked MoE checkpoint 在
tp>1/ep>1下改动前后的torch.cuda.max_memory_allocated对比,并在 PR 描述中写明可接受的峰值上升幅度,便于回滚判断。若确认峰值上升:优先推动上游AutoLoader支持按 dim0 切片产出或分块 copyout,在传输前完成切分;短期至少把「一个完整 stacked 张量常驻 + 一份 clone」计入_fastsafetensors_transient_budget_bytes,避免预检通过而加载期 OOM。另外database.py:363对所有 expert 都 clone,而loader.py:472-474随即丢弃非本 rank 的 key,ep>1下建议让适配层只 cloneget_selected_experts命中的 expert(把stacked_key_config的 value 扩展为携带本 rank expert 集合)。若结论是依赖上游 bounded batch buffer 抵消该回退,请在database.py:358-362注释与配置文档中写明这一前提及所需配置。
- 建议:给出同一 stacked MoE checkpoint 在
- cuda13_arm wheel 分发的 flashinfer 变体与实际链接的变体不一致 @
rtp_llm/libs/BUILD:78- 建议:让打包侧与链接侧使用同一平台判据。根
BUILD:44-47的注释表明using_cuda13_arm本意就是启用flashinfer_cpp_cu13,若确认如此,请先在flashinfer_deps()增加using_cuda13_arm分支;否则从本select()去掉//:using_cuda13_arm,或为 ARM 单独声明并分发@flashinfer_cpp变体的.so(注意.bazelrc:159-164的注释声明 x86 的 define 与 ARM 路径解绑,两处意图目前不一致,需先统一)。建议把「哪个平台用哪个 flashinfer 仓库」抽成arch_config中的单一常量,供flashinfer_deps()与本 BUILD 共同消费。并补一条 cuda13_arm 产物校验:确认 wheel 构建通过、libth_transformer.so的 DT_NEEDED 全部能在 wheel 内解析且来自同一变体;同时确认flashinfer_cu13.BUILD:12-15硬编码sm_90/sm_90a的sm90_cuda_copts在 SM100 ARM 工具链上可编译。
- 建议:让打包侧与链接侧使用同一平台判据。根
Non-blocking Suggestions
P2
- 平台无关宏无条件引用 CUDA-13 专用仓库,非 cuda13 平台通配构建会被动拉取 @
arch_config/arch_select.bzl:28- 建议:让声明侧与消费侧的平台条件对齐,任选其一:给这批拷贝目标加
tags = ["manual"](可在copy_so增加可选参数传入),使通配模式不再选中;或改用rtp_llm/libs/BUILD已有的copy_files+select模式(参考copy_hf3fs_libs、copy_acclep_libs,bazel/defs.bzl的实现对空 srcs 返回空 depset,天然支持「非目标平台不产出」);或让 genrule 的srcs走select()在非 cuda13 下取空。核心是不让平台专用概念泄漏进平台无关宏。并在 CI 对 rocm/cpu 至少跑一次覆盖//rtp_llm/libs的分析,确认非 cuda13 平台不会被动拉取 CUDA13 依赖。
- 建议:让声明侧与消费侧的平台条件对齐,任选其一:给这批拷贝目标加
- 11 个 flashinfer 目标名三处手抄,无单一来源且漏发不可检测 @
arch_config/arch_select.bzl:16- 建议:把清单收敛为
arch_config显式导出的单一来源(例如常量CUDA13_FLASHINFER_SO_TARGETS),copy_all_so()遍历它、rtp_llm/libs/BUILDload 后以[":lib%s_so" % t for t in ...]派生——这样任何 override 实现缺失该符号都会在 load 期显式失败,而不是等 cuda13 select 命中才暴露;并在 PR 描述中标注是否需要配套变更其它@arch_config实现。同时补一条轻量打包校验(wheel 内rtp_llm/libs/libflashinfer_*.so数量与清单长度一致,或libth_transformer.so的 DT_NEEDED 全部可解析),把「漏发一个库」从运行期故障前移到构建期失败。
- 建议:把清单收敛为
- transient 显存预算无下界,异常分支未覆盖且未纳入 OSError @
rtp_llm/model_loader/loader.py:359- 建议:对库返回值取下限并计入 RTP 侧已知瞬时量,例如
transient_mem = max(upstream_estimate, k * max_file_size) + 单个 stacked tensor 大小 + inline 量化余量,避免阈值退化为恒真。把 except 收口为except Exception(或至少纳入OSError)并在注释说明捕获范围的取舍,去掉冗余的ModuleNotFoundError。补三条用例:注入不含load_config的 fake 模块、load_config()抛异常(用assertLogs校验 warning 且返回3 * max_file_size)、estimated_peak_device_bytes=0的下界行为。
- 建议:对库返回值取下限并计入 RTP 侧已知瞬时量,例如
- load_config() 早于 NOGDS 环境归一化被调用,预检预算与实际配置不一致 @
rtp_llm/model_loader/loader.py:356- 建议:把
FASTSAFETENSORS_NOGDS→FASTSAFETENSORS_CONFIG_JSON的归一化提前到任何load_config()调用之前(例如在加载入口一次性初始化),保证预检与实际构造读到同一份配置;或让_fastsafetensors_transient_budget_bytes显式接受最终生效的配置对象,消除对调用顺序的隐式依赖。同时确认上游load_config()是否做进程级缓存并在注释中记录结论。
- 建议:把
- copyout 谓词跨 rank 不对称,EP>1 语义边界未确认且无多卡覆盖 @
rtp_llm/model_loader/loader.py:467- 建议:先确认并在
loader.py:467调用处注释中写明上游对 rank 间不一致local_copyout_filter的语义边界(是否仅作用于本 rank 的 copy-out、不参与集合通信调度),并引用上游文档与版本;若语义要求对称,改为传入各 rank key 的并集。补一条ep_size > 1的多进程单测或 smoke,验证非本 rank expert 被过滤后权重仍完整、无 hang——这是本次唯一真正改变多卡加载行为的分支,却完全没有多卡覆盖。
- 建议:先确认并在
- fallback 慢路径零覆盖且线上不可观测,_fallback_count 自增后从未使用 @
rtp_llm/model_loader/loader.py:523- 建议:补一条负向用例:让 fake database 只产出部分 key、令
is_collection_complete()返回 False,断言确实走到DatabaseTensorSource且权重最终仍正确落位。生产侧把_fallback_count与回退权重名汇总为一条 warning 或指标(例如「N/M 个权重回退到 database 路径」),使该退化在线上可观测;若不打算使用该计数,请直接删除这个死变量。
- 建议:补一条负向用例:让 fake database 只产出部分 key、令
- 新增 loader 侧用例把生产边界整体 mock,关键联动无覆盖 @
rtp_llm/model_loader/test/test_stacked_moe_weight.py:315- 建议:用真实
MoeAtomicWeight/TensorCollector构造最小 weight tree,让_build_stacked_key_config与 filter 在同一用例内联动,断言_build_fastsafetensors_local_copyout_keys的结果是下游实际消费 key 的超集、并断言最终写入model_weights的张量值;补一条stacked_key_config为空时 copyout 集合等于tensor_to_weight_map键集的用例。把_build_fastsafetensors_local_copyout_keys保留为纯函数测试,选路类测试下移到集成/smoke 层以减少对私有方法的 mock。
- 建议:用真实
- per-expert 拆分用例以纯 Python fake 替代张量,未校验切分语义与边界 @
rtp_llm/utils/test/ckpt_database_test.py:119- 建议:改用真实
torch张量(如torch.arange(6).reshape(3, 2))断言每个专家切片的数值、dtype 与data_ptr不同于原张量,真正验证「按专家切分 + 拷贝解耦」;补 1-2 个边界用例(template 无{expert_id}、shape[0]==1)。另建议新增一条不打桩、直接 import 真实fastsafetensors校验AutoLoader构造签名与load_config()字段的用例(可按依赖存在性 skip),使上游 API 漂移在 CI 而非部署期暴露——当前FakeAutoLoader.__init__(test:143)由测试自行固化了上游签名契约。
- 建议:改用真实
- 文档把生效前提写成显式 LOAD_METHOD,遗漏默认 auto 同样走 AutoLoader @
docs/backend/server_arguments.md:208- 建议:将前提改为「当最终选定 fastsafetensors 加载方式时(显式
LOAD_METHOD=fastsafetensors,或默认auto自动选中)」,并简述 auto 的选中条件(safetensors 权重、非 CPU 加载、张量名唯一、显存充足且已安装 fastsafetensors)或链接到--load_method表格行,使读者明确默认部署也在该配置面覆盖范围内。
- 建议:将前提改为「当最终选定 fastsafetensors 加载方式时(显式
- 未记录 RTP 侧加载调优默认值移交上游这一运维可见行为变化 @
docs/backend/server_arguments.md:206- 建议:在小节中补一段:RTP 不再覆盖 fastsafetensors 的 buffer / shm / ordering 参数,相关默认值由所装版本决定;若原先依赖较大 buffer 行为,给出通过
FASTSAFETENSORS_CONFIG_JSON显式配置bbuf_size_kb等价项的示例。按本页既有先例在docs/release/breaking-changes.md登记该加载后端切换与默认值移交。另建议把FASTSAFETENSORS_CONFIG_JSON、FASTSAFETENSORS_CONFIG、FASTSAFETENSORS_NOGDS各补一行进## Load Config表格(Description 指向本小节),与本页「环境变量挂在表格行内」(201-204 行)的既有约定保持一致,便于按表格检索或结构化解析。
- 建议:在小节中补一段:RTP 不再覆盖 fastsafetensors 的 buffer / shm / ordering 参数,相关默认值由所装版本决定;若原先依赖较大 buffer 行为,给出通过
- NOGDS 兼容开关的优先级结论未验证,且覆写不可逆污染进程级 env @
docs/backend/server_arguments.md:223- 建议:三者任选:(1)补一条同时设置
FASTSAFETENSORS_CONFIG文件路径与FASTSAFETENSORS_NOGDS=1的用例,实测并锁定优先级;(2)把绝对化表述改为限定表述,注明该优先级由上游实现与版本决定、RTP 并不读取FASTSAFETENSORS_CONFIG;(3)在覆写FASTSAFETENSORS_CONFIG_JSON的同时一并清除FASTSAFETENSORS_CONFIG,使结论自洽而不依赖外部实现细节。同时在文档中说明该覆写是进程级、持续生效且会丢弃用户原有 inline JSON,并考虑改为仅在构造期临时生效(如try/finally恢复原值),避免不可逆污染进程状态。
- 建议:三者任选:(1)补一条同时设置
P3
- use_tqdm_on_load 成为死参数,加载进度可观测性静默丢失 @
rtp_llm/utils/database.py:312- 建议:若上游
AutoLoader支持进度显示则透传该参数;否则从fastsafetensors_weights_iterator签名与loader.py调用点一并移除,或在注释中说明该职责已由上游配置接管。同时确认loader.py:496-503每 5000 个 tensor 的周期性日志足以替代原进度条,必要时下调打印间隔或补充剩余量信息。
- 建议:若上游
- budget helper docstring 字段名与实现不符,日志风格与参考值表述易误读 @
rtp_llm/model_loader/loader.py:352- 建议:docstring 中的
max_batch_bytes改为实际读取的estimated_peak_device_bytes;日志中明确max_file_mem仅为参考值(或与transient_mem合并表述);新增日志统一为文件内既有 f-string 风格。
- 建议:docstring 中的
- 测试文件 Covers 清单与新增用例归类未同步 @
rtp_llm/model_loader/test/test_stacked_moe_weight.py:3- 建议:更新文件头
Covers:列表,并把两个新用例移到独立测试类(例如TestLocalCopyoutKeys),使测试类边界与被测方法对应,便于后续按方法定位失败用例。另建议把这批无 GPU 依赖的纯 CPU 用例拆到不带H20tag 的 py_test 目标,使其能在 cuda13 / ARM / ROCm lane 也执行——这恰是本 PR 两个 P1 最需要覆盖的平台。
- 建议:更新文件头
- select 并列分支未复用仓库既有 config_setting_group 约定 @
rtp_llm/libs/BUILD:76- 建议:在根
BUILD增加selects.config_setting_group(name = "using_cuda13", match_any = [":using_cuda13_x86", ":using_cuda13_arm"]),本文件改为单分支,后续新增 CUDA13 平台也不必逐个select()补分支。注意该归并须与上文 ARM flashinfer 变体问题一并确认——只有两平台确实使用同一变体时才可归并。
- 建议:在根
- CUDA13 打包变更与本 PR 主题无关,缺少动机说明与构建证据 @
arch_config/arch_select.bzl:15- 建议:将 CUDA-13 FlashInfer 打包拆成独立提交或 PR;若保留在本 PR,请在 description 中补充动机(cuda13 运行期缺少
libflashinfer_*.so的现象与复现方式),并附上 cuda13 / cuda13_arm 两个配置下 wheel 目标构建通过、wheel 内.so清单符合预期的证据。同时在循环上方补一行注释说明为何只有 cuda13 需要在本文件复制(例如 cuda12 由 override 版arch_config或镜像打包补齐),避免后续读者误以为 cuda12 wheel 也缺库;若 cuda12 确实同样缺失,应按前述建议做成随配置 select 的统一路径。
- 建议:将 CUDA-13 FlashInfer 打包拆成独立提交或 PR;若保留在本 PR,请在 description 中补充动机(cuda13 运行期缺少
Checklist Findings (21 fail / 48 total)
General Principles Checklist
- [6.1] Architecture — 依赖方向:无循环依赖/跨层惊喜 → issue
平台无关宏无条件引用 CUDA-13 专用仓库,非 cuda13 平台通配构建会被动拉取
copy_all_so()在rtp_llm/libs/BUILD:6被无 select 包裹地调用,因此 11 个 genrule 在 cpu/arm/rocm/cuda12/cuda12_9/ppu 等所有配置下都存在;srcs硬编码@flashinfer_cpp_cu13,而copy_so(bazel/defs.bzl:1-10)的 tags 只有no-remote、无manual。此前该宏只引用平台无关的@rtp_llm//:...。一旦出现包级通配(//rtp_llm/libs/...、//...、bazel query)就会分析这些目标,触发该仓库 git clone + 15 个补丁并按sm_90/sm_90acopts 编译,还引入def_cu13.bzl:4-14的@local_config_cuda//cuda:cudart,在 ROCm/CPU 上不可用。同文件flashinfer_deps()用 select 分流,本变更偏离该约定。 - [6.1] Architecture — 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全 → issue
NOGDS 兼容开关的优先级结论未验证,且覆写不可逆污染进程级 env
223-224 行断言该开关「therefore takes priority over other fastsafetensors configuration」,但database.py:337-344只覆写FASTSAFETENSORS_CONFIG_JSON,从不读取也不触碰FASTSAFETENSORS_CONFIG文件路径;该绝对化结论完全依赖 209-210 行「inline JSON 优先于文件」这一上游行为,仓内无代码或测试可佐证,新增用例ckpt_database_test.py:94-106更在 setUp 中同时清空两个变量、恰好回避了文档承诺的组合。此外覆写直接写进os.environ且从不恢复(test:258-268 断言用户原值{"loader":"fuse-shm"}被静默替换),同进程后续 ViT / draft / EPLB 加载亦持续受影响,文档未提示该持久性。 - [6.1] Architecture — 分层边界:新概念在正确层级,不泄漏内部 → issue
平台无关宏无条件引用 CUDA-13 专用仓库,非 cuda13 平台通配构建会被动拉取
copy_all_so()在rtp_llm/libs/BUILD:6被无 select 包裹地调用,因此 11 个 genrule 在 cpu/arm/rocm/cuda12/cuda12_9/ppu 等所有配置下都存在;srcs硬编码@flashinfer_cpp_cu13,而copy_so(bazel/defs.bzl:1-10)的 tags 只有no-remote、无manual。此前该宏只引用平台无关的@rtp_llm//:...。一旦出现包级通配(//rtp_llm/libs/...、//...、bazel query)就会分析这些目标,触发该仓库 git clone + 15 个补丁并按sm_90/sm_90acopts 编译,还引入def_cu13.bzl:4-14的@local_config_cuda//cuda:cudart,在 ROCm/CPU 上不可用。同文件flashinfer_deps()用 select 分流,本变更偏离该约定。 - [6.1] Architecture — 可观测性:日志/指标/超时可操作、非噪声 → issue
budget helper docstring 字段名与实现不符,日志风格与参考值表述易误读
docstring(loader.py:352)写when max_batch_bytes is unset,实现读的却是estimated_peak_device_bytes(:359),max_batch_bytes在代码中并不存在,维护者会去上游找错字段。max_file_mem(:334)改动后仅用于日志(:341),易被误读为仍参与判定。新增日志(:445-449)用%-占位风格,与同函数内其余 f-string 不一致。 - [6.1] Architecture — 回滚路径:风险行为存在运维回滚手段 → issue
未记录 RTP 侧加载调优默认值移交上游这一运维可见行为变化
旧实现固定传bbuf_size_kb=1024*1024*2、use_shm=not use_nogds、nogds=use_nogds;新实现只传pg / files / device / local_copyout_filter(database.py:345-350),buffer 大小、batching、读取粒度、tensor ordering 全部改由所装 wheel 的默认值决定。这是加载耗时与峰值内存的可观察变化,而新小节只写「怎么配」,未说明「默认行为已改变」,也未给出恢复旧读放大行为的等价 JSON。同页第 9 行已有「运维可见默认值变更需在../release/breaking-changes.md说明」的先例。 - [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue
NOGDS 兼容开关的优先级结论未验证,且覆写不可逆污染进程级 env
223-224 行断言该开关「therefore takes priority over other fastsafetensors configuration」,但database.py:337-344只覆写FASTSAFETENSORS_CONFIG_JSON,从不读取也不触碰FASTSAFETENSORS_CONFIG文件路径;该绝对化结论完全依赖 209-210 行「inline JSON 优先于文件」这一上游行为,仓内无代码或测试可佐证,新增用例ckpt_database_test.py:94-106更在 setUp 中同时清空两个变量、恰好回避了文档承诺的组合。此外覆写直接写进os.environ且从不恢复(test:258-268 断言用户原值{"loader":"fuse-shm"}被静默替换),同进程后续 ViT / draft / EPLB 加载亦持续受影响,文档未提示该持久性。 - [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue
copyout 谓词跨 rank 不对称,EP>1 语义边界未确认且无多卡覆盖
required_checkpoint_keys来自tensor_to_weight_map(loader.py:578-627),其 MoE 部分经MoeAtomicWeight.get_tensor_names(ffn_weight.py:706-720)→LoadConfig.get_selected_experts(load_config.py:89-98)按ep_rank切片,故ep_size>1时非 stacked 的 per-expert key 集合随 rank 不同(stacked 原始 key 对称)。该非对称谓词被无条件传入以进程组为基础的AutoLoader,而torch_patch.py:33-38明确记载 AutoLoader 在多 rank 加载时通过pg.broadcast搬运权重,即内部确有跨 rank 集合通信。若上游把 copyout 判定与集合调度绑定,可能出现广播缺参与方而 hang。新增用例全部为单 rank + fake loader,无任何ep_size>1覆盖。 - [6.1] Quality — Commit 原子、message 与行为匹配 → issue
CUDA13 打包变更与本 PR 主题无关,缺少动机说明与构建证据
本 PR 其余 8 个文件全部围绕 model_loader / utils 的 fastsafetensors 改造(loader.py、per_expert_parallel_loader.py、database.py、torch_patch.py及对应测试、server_arguments.md),本文件新增的 CUDA-13 FlashInfer wheel 打包与该主题无逻辑关联,混在同一提交中会削弱 CI 二分定位能力,改动动机在代码中亦无任何注释说明。另外仅 cuda13 补齐 flashinfer 动态库,而flashinfer.BUILD:133-143为 cuda12 系列产出同名同构目标、whl_package_libs默认分支仍为空,代码中没有任何线索解释该差异。 - [6.1] Quality — Mega-PR 已拆分为独立变更 → issue
CUDA13 打包变更与本 PR 主题无关,缺少动机说明与构建证据
本 PR 其余 8 个文件全部围绕 model_loader / utils 的 fastsafetensors 改造(loader.py、per_expert_parallel_loader.py、database.py、torch_patch.py及对应测试、server_arguments.md),本文件新增的 CUDA-13 FlashInfer wheel 打包与该主题无逻辑关联,混在同一提交中会削弱 CI 二分定位能力,改动动机在代码中亦无任何注释说明。另外仅 cuda13 补齐 flashinfer 动态库,而flashinfer.BUILD:133-143为 cuda12 系列产出同名同构目标、whl_package_libs默认分支仍为空,代码中没有任何线索解释该差异。 - [6.1] Quality — PR description 说明动机与设计 → issue
CUDA13 打包变更与本 PR 主题无关,缺少动机说明与构建证据
本 PR 其余 8 个文件全部围绕 model_loader / utils 的 fastsafetensors 改造(loader.py、per_expert_parallel_loader.py、database.py、torch_patch.py及对应测试、server_arguments.md),本文件新增的 CUDA-13 FlashInfer wheel 打包与该主题无逻辑关联,混在同一提交中会削弱 CI 二分定位能力,改动动机在代码中亦无任何注释说明。另外仅 cuda13 补齐 flashinfer 动态库,而flashinfer.BUILD:133-143为 cuda12 系列产出同名同构目标、whl_package_libs默认分支仍为空,代码中没有任何线索解释该差异。 - [6.1] Quality — 逻辑变更未混入无关格式化 → issue
测试文件 Covers 清单与新增用例归类未同步
文件头Covers:(test:3-7)仍只列出StackSplitTensorSource、MoeAtomicWeight._build_split_config、ModelLoader._build_stacked_key_config三项,未包含新增覆盖的_build_fastsafetensors_local_copyout_keys与_fastsafetensors_transient_budget_bytes。同时 302 / 315 行两个新用例被放进 236 行的TestBuildStackedKeyConfig,而该类 docstring 明确写着Tests for ModelLoader._build_stacked_key_config,实际测的却是另外两个方法以及_load_from_fastsafetensor的整体流程。 - [6.1] Software Engineering — DRY:重复非平凡逻辑被抽取或显式复用 → issue
select 并列分支未复用仓库既有 config_setting_group 约定
新增select()为//:using_cuda13_x86与//:using_cuda13_arm写了两个内容完全相同的分支。根BUILD:116-124已建立相反约定并写明理由:「Selects whose cuda13_x86 behavior is identical to cuda12_9_x86 use this group so we don't have to add a parallel branch to every select()」,即优先用selects.config_setting_group归并等价平台,而不是在每个select()里并列分支。 - [6.1] Software Engineering — KISS/YAGNI:无投机性抽象 → issue
use_tqdm_on_load 成为死参数,加载进度可观测性静默丢失
旧实现把use_tqdm_on_load透传给ParallelLoader(use_tqdm_on_load=...);新实现AutoLoader(pg, hf_weights_files, device=device, local_copyout_filter=...)(database.py:345-350)不再使用该参数,但fastsafetensors_weights_iterator签名(:312)、内层iterator(device, use_tqdm_on_load)(:318、:371)与调用方loader.py:463-465(位置传参True)都仍保留它。结果是参数保留却无任何作用,权重加载进度条静默消失,长时间加载缺少进展反馈,也给后续维护者留下误导性接口。 - [6.1] Software Engineering — OCP:本地扩展点优先于修改中心逻辑 → issue
平台无关宏无条件引用 CUDA-13 专用仓库,非 cuda13 平台通配构建会被动拉取
copy_all_so()在rtp_llm/libs/BUILD:6被无 select 包裹地调用,因此 11 个 genrule 在 cpu/arm/rocm/cuda12/cuda12_9/ppu 等所有配置下都存在;srcs硬编码@flashinfer_cpp_cu13,而copy_so(bazel/defs.bzl:1-10)的 tags 只有no-remote、无manual。此前该宏只引用平台无关的@rtp_llm//:...。一旦出现包级通配(//rtp_llm/libs/...、//...、bazel query)就会分析这些目标,触发该仓库 git clone + 15 个补丁并按sm_90/sm_90acopts 编译,还引入def_cu13.bzl:4-14的@local_config_cuda//cuda:cudart,在 ROCm/CPU 上不可用。同文件flashinfer_deps()用 select 分流,本变更偏离该约定。 - [6.1] Software Engineering — SRP:模块/类职责单一 → issue
测试文件 Covers 清单与新增用例归类未同步
文件头Covers:(test:3-7)仍只列出StackSplitTensorSource、MoeAtomicWeight._build_split_config、ModelLoader._build_stacked_key_config三项,未包含新增覆盖的_build_fastsafetensors_local_copyout_keys与_fastsafetensors_transient_budget_bytes。同时 302 / 315 行两个新用例被放进 236 行的TestBuildStackedKeyConfig,而该类 docstring 明确写着Tests for ModelLoader._build_stacked_key_config,实际测的却是另外两个方法以及_load_from_fastsafetensor的整体流程。 - [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue
copyout 谓词跨 rank 不对称,EP>1 语义边界未确认且无多卡覆盖
required_checkpoint_keys来自tensor_to_weight_map(loader.py:578-627),其 MoE 部分经MoeAtomicWeight.get_tensor_names(ffn_weight.py:706-720)→LoadConfig.get_selected_experts(load_config.py:89-98)按ep_rank切片,故ep_size>1时非 stacked 的 per-expert key 集合随 rank 不同(stacked 原始 key 对称)。该非对称谓词被无条件传入以进程组为基础的AutoLoader,而torch_patch.py:33-38明确记载 AutoLoader 在多 rank 加载时通过pg.broadcast搬运权重,即内部确有跨 rank 集合通信。若上游把 copyout 判定与集合调度绑定,可能出现广播缺参与方而 hang。新增用例全部为单 rank + fake loader,无任何ep_size>1覆盖。 - [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue
NOGDS 兼容开关的优先级结论未验证,且覆写不可逆污染进程级 env
223-224 行断言该开关「therefore takes priority over other fastsafetensors configuration」,但database.py:337-344只覆写FASTSAFETENSORS_CONFIG_JSON,从不读取也不触碰FASTSAFETENSORS_CONFIG文件路径;该绝对化结论完全依赖 209-210 行「inline JSON 优先于文件」这一上游行为,仓内无代码或测试可佐证,新增用例ckpt_database_test.py:94-106更在 setUp 中同时清空两个变量、恰好回避了文档承诺的组合。此外覆写直接写进os.environ且从不恢复(test:258-268 断言用户原值{"loader":"fuse-shm"}被静默替换),同进程后续 ViT / draft / EPLB 加载亦持续受影响,文档未提示该持久性。 - [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue
per-expert 拆分用例以纯 Python fake 替代张量,未校验切分语义与边界
test_stacked_experts_are_cloned_and_renamed的FakeStackedTensor.shape = (3, 2)、clone()返回object()(test:136-141),只验证键名改写顺序与 clone 调用次数。文件已 import torch,却没有任何一处断言tensor[expert_id]取到的数据、dtype、shape 与原 stacked 张量对应,也未验证 clone 结果与原 batch buffer 解耦——而database.py:358-362的注释明确指出 clone 的目的正是「loader 迭代进入下一批后可能释放当前 batch buffer」。同时缺少边界用例:template 缺少{expert_id}占位符导致format静默不生效、shape[0] == 1、shape[0]与预期专家数不一致。
RTP-LLM Checklist
- [I] 代码质量 — 删除或重命名内部 file、registry entry、model name、metric enum、op binding、plugin symbol 时,必须全仓搜索消费者,并提供替代实现、迁移说明或 smoke 覆盖;只有暴露到 HTTP/RPC/config/persisted format 时才按外部兼容性处理 → issue
删除逐 expert 广播后 stacked MoE 多 rank 峰值显存回退且无实测
被删类 docstring 写明其唯一价值:Peak GPU memory during broadcast drops from the full stacked tensor size to a single expert slice;pg.size()>1时非源 rank 只分配单 expert 空张量再逐个pg.broadcast。新实现把device交给AutoLoader(database.py:345-350),iterate_weights()产出设备上完整[num_experts, ...]后才tensor[expert_id].clone()(:363-367),且tensor在内层循环全程存活。loader.py:398有意让 stacked 原始 key 在每个 rank 通过过滤;torch_patch.py:33-38亦记载 AutoLoader 多 rank 加载时用pg.broadcast搬运权重。qwen3_next_weight.py:600与 `qwen3_vl_m - [I] 代码质量 — 同一功能用统一工具函数 → issue
select 并列分支未复用仓库既有 config_setting_group 约定
新增select()为//:using_cuda13_x86与//:using_cuda13_arm写了两个内容完全相同的分支。根BUILD:116-124已建立相反约定并写明理由:「Selects whose cuda13_x86 behavior is identical to cuda12_9_x86 use this group so we don't have to add a parallel branch to every select()」,即优先用selects.config_setting_group归并等价平台,而不是在每个select()里并列分支。
Python Static-First Checklist
- [P.G] 测试规范 — mock/fake/stub 不得替代本次声称覆盖的生产边界 → issue
per-expert 拆分用例以纯 Python fake 替代张量,未校验切分语义与边界
test_stacked_experts_are_cloned_and_renamed的FakeStackedTensor.shape = (3, 2)、clone()返回object()(test:136-141),只验证键名改写顺序与 clone 调用次数。文件已 import torch,却没有任何一处断言tensor[expert_id]取到的数据、dtype、shape 与原 stacked 张量对应,也未验证 clone 结果与原 batch buffer 解耦——而database.py:358-362的注释明确指出 clone 的目的正是「loader 迭代进入下一批后可能释放当前 batch buffer」。同时缺少边界用例:template 缺少{expert_id}占位符导致format静默不生效、shape[0] == 1、shape[0]与预期专家数不一致。
Strengths
- 上游耦合面显著收窄:不再依赖
fb._get_rank_lidx、fb.rank_loaders、fb.instantiated、fb.auto_mem_delete、factory.free_dev_ptrs等一整批 fastsafetensors 内部字段,后续升级 wheel 的破裂面大幅缩小。 _build_fastsafetensors_local_copyout_keys(loader.py:386-398)正确处理「过滤早于 per-expert 展开生效」的时序:stacked 条目按原始 ckpt key 保留,展开留在 database 适配层,docstring 写清了这一非显然约束并配有直测用例。- 用
frozenset.__contains__作谓词,判定 O(1)、集合不可变,不会把可变状态泄漏给第三方 loader;对 dense 与非 stacked per-expert 权重是实质性的设备内存与带宽收益。 - 被删闸门以
startswith("0.1.19")判定,意味着 cuda13 所装的0.1.20+ali下 stacked MoE 原本必然抛 RuntimeError,本 PR 实际修掉了该平台的加载阻塞。 - 删除收尾干净:全仓已无被删符号引用,
torch_patch.py:29-39的 UE8M0 broadcast 注释同步改写为AutoLoader,未留指向已删类的悬空描述。 - 显存预检保留 legacy
3 * max_file_size兜底,并在同一条日志中打印transient_mem与enough,AUTO 选路原因可定位。 - 文档主动披露
FASTSAFETENSORS_NOGDS=1覆写FASTSAFETENSORS_CONFIG_JSON这一反直觉行为,与database.py:337-344实现逐字一致,未粉饰副作用,代码侧还配了 warning。 ckpt_database_test.py:91-117对sys.modules["fastsafetensors"]与三个FASTSAFETENSORS_*env 做了「原本存在/原本不存在」的区分式保存恢复;在生产逻辑会改写进程级 env 的前提下,这是必要的隔离防护。rtp_llm/libs/BUILD:76-80用select()把新增.so限定在 cuda13,cuda12/cuda12_9/ROCm 的 wheel 内容不变,回滚面小。
| # key per expert. Clone each slice because the loader can | ||
| # release the current batch buffer after iteration moves | ||
| # on to the next batch. | ||
| for expert_id in range(tensor.shape[0]): |
There was a problem hiding this comment.
[P1] 删除逐 expert 广播后 stacked MoE 多 rank 峰值显存回退且无实测
被删类 docstring 写明其唯一价值:Peak GPU memory during broadcast drops from the full stacked tensor size to a single expert slice;pg.size()>1 时非源 rank 只分配单 expert 空张量再逐个 pg.broadcast。新实现把 device 交给 AutoLoader(database.py:345-350),iterate_weights() 产出设备上完整 [num_experts, ...] 后才 tensor[expert_id].clone()(:363-367),且 tensor 在内层循环全程存活。loader.py:398 有意让 stacked 原始 key 在每个 rank 通过过滤;torch_patch.py:33-38 亦记载 AutoLoader 多 rank 加载时用 pg.broadcast 搬运权重。qwen3_next_weight.py:600 与 `qwen3_v...
建议: 给出同一 stacked MoE checkpoint 在 tp>1 / ep>1 下改动前后的 torch.cuda.max_memory_allocated 对比,并在 PR 描述中写明可接受的峰值上升幅度,便于回滚判断。若确认峰值上升:优先推动上游 AutoLoader 支持按 dim0 切片产出或分块 copyout,在传输前完成切分;短期至少把「一个完整 stacked 张量常驻 + 一份 clone」计入 _fastsafetensors_transient_budget_bytes,避免预检通过而加载期 OOM。另外 database.py:363 对所有 expert 都 clone,而 loader.py:472-474 随即丢弃非本 rank 的 key,ep>1 下建议让适配层只 clone get_selected_experts 命中的 expert(把 stacked_key_config 的 value 扩展为携带本 rank expert 集合)。若结论是依赖上游 bounded batch buffer 抵消该回退,请在 database.py:358-362 注释与配置文档中写明这一前提及所需配置。
Checklist: [I] 删除或重命名内部 file、registry entry、model name、metric enum、op binding、plugin symbol 时,必须全仓搜索消费者,并提供替代实现、迁移说明或 smoke 覆盖;只有暴露到 HTTP/RPC/config/persisted format 时才按外部兼容性处理
There was a problem hiding this comment.
Addressed in aa88afea4 with matching FastSafeTensors wrapper commit e6e39437.
The production default is now the bounded-memory per-expert path: the source rank clones one dim-0 expert slice, every rank executes the same slice broadcast sequence, and rank-local filtering is applied only after each collective. Non-source ranks no longer materialize the full stacked tensor. A cross-rank hash over raw key/template/shape fails before collectives if the split plans differ.
The previous full-stacked behavior is intentionally retained for controlled speed/memory comparison via RTP_FASTSAFETENSORS_STACKED_MOE_MODE=full-stacked; it is not the default. Ordinary non-stacked tensors remain on the existing bucket/rank-local path.
Exact wheel CI 278696 / 67401633 (revision e6e39437...) completed 16/16 jobs successfully; CUDA 12.9 x86 installed-wheel tests report 293 passed / 11 skipped. CPU coverage includes value/storage lifetime, interleaved ordering, expanded-key filtering, and capability forwarding. The new two-rank NCCL case exists but was skipped by CI workers without two visible GPUs. The previous DeepSeek test Pods were already recycled, so real multi-rank NCCL and per-expert vs full-stacked peak-memory/performance PK remain explicit pending gates and are not being inferred from CI success.
| ], | ||
| ] + select({ | ||
| "//:using_cuda13_x86": cuda13_flashinfer_shared_libs, | ||
| "//:using_cuda13_arm": cuda13_flashinfer_shared_libs, |
There was a problem hiding this comment.
[P1] cuda13_arm wheel 分发的 flashinfer 变体与实际链接的变体不一致
whl_package_libs(BUILD:76-80)把 //:using_cuda13_x86 与 //:using_cuda13_arm 都指向 cuda13_flashinfer_shared_libs,其 .so 由 copy_all_so()(arch_select.bzl:15-28)从 @flashinfer_cpp_cu13 拷出;但 flashinfer_deps()(:164-171)只把 x86 路由到该仓库,ARM 落 //conditions:default → @flashinfer_cpp。两仓同 commit,cu13 多 0011-0015 五个补丁(含两个 kernel-visibility,deps/git.bzl:93-108),sub_lib 输出同名 libflashinfer_*.so 且被 flashinfer 以 srcs 引用(flashinfer_cu13.BUILD:199-214)→ 产生 DT_NEEDED;配合根 BUILD:194 的 $ORIGIN,A...
建议: 让打包侧与链接侧使用同一平台判据。根 BUILD:44-47 的注释表明 using_cuda13_arm 本意就是启用 flashinfer_cpp_cu13,若确认如此,请先在 flashinfer_deps() 增加 using_cuda13_arm 分支;否则从本 select() 去掉 //:using_cuda13_arm,或为 ARM 单独声明并分发 @flashinfer_cpp 变体的 .so(注意 .bazelrc:159-164 的注释声明 x86 的 define 与 ARM 路径解绑,两处意图目前不一致,需先统一)。建议把「哪个平台用哪个 flashinfer 仓库」抽成 arch_config 中的单一常量,供 flashinfer_deps() 与本 BUILD 共同消费。并补一条 cuda13_arm 产物校验:确认 wheel 构建通过、libth_transformer.so 的 DT_NEEDED 全部能在 wheel 内解析且来自同一变体;同时确认 flashinfer_cu13.BUILD:12-15 硬编码 sm_90/sm_90a 的 sm90_cuda_copts 在 SM100 ARM 工具链上可编译。
There was a problem hiding this comment.
已在 05413c2 修复:flashinfer_deps() 现在对 using_cuda13_arm 和 using_cuda13_x86 都选择 @flashinfer_cpp_cu13//:flashinfer,与 whl_package_libs 通过 cuda13_flashinfer_shared_libs 打包 cu13 变体的行为一致。内源对应选择原本已经正确,本次只修改外源一处。已通过 git diff --check、pre-commit,并用静态断言核对 ARM/x86 的 link/package 映射一致。当前本机没有 CUDA13 ARM 开发容器,因此未把真实 ARM Bazel/wheel 链接验证冒充为已通过;该项继续由 PR CI 验证。
| from fastsafetensors import load_config | ||
|
|
||
| config = load_config() | ||
| estimate = getattr(config, "estimated_peak_device_bytes", None) |
There was a problem hiding this comment.
[P2] transient 显存预算无下界,异常分支未覆盖且未纳入 OSError
loader.py:360 在 estimate 非 None 时原样返回(包括 0),而 loader.py:338 以 enough = (free_mem - model_mem) > transient_mem 准入:库返回 0 时判定退化为 free_mem > model_mem,原 3 * max_file_size 的安全余量被完全抹掉。该估算也只覆盖库自身 batch buffer,不含 database.py:363-367 的逐 expert clone、完整 stacked 张量本体与 inline FP8 临时 buffer。loader.py:361 的 except 元组含冗余的 ModuleNotFoundError(ImportError 子类)却不含 OSError,而本 PR 文档新增的 FASTSAFETENSORS_CONFIG=<file> 在文件缺失/损坏时 load_config() 很可能抛 OSError,会穿透兜底崩掉整个加载。`TestFastsafetensorsTran...
建议: 对库返回值取下限并计入 RTP 侧已知瞬时量,例如 transient_mem = max(upstream_estimate, k * max_file_size) + 单个 stacked tensor 大小 + inline 量化余量,避免阈值退化为恒真。把 except 收口为 except Exception(或至少纳入 OSError)并在注释说明捕获范围的取舍,去掉冗余的 ModuleNotFoundError。补三条用例:注入不含 load_config 的 fake 模块、load_config() 抛异常(用 assertLogs 校验 warning 且返回 3 * max_file_size)、estimated_peak_device_bytes=0 的下界行为。
| """ | ||
| legacy_budget = 3 * max_file_size | ||
| try: | ||
| from fastsafetensors import load_config |
There was a problem hiding this comment.
[P2] load_config() 早于 NOGDS 环境归一化被调用,预检预算与实际配置不一致
AUTO 预检链路(loader.py:266 → :335 → :356-358)调用 load_config() 读取预算,而 FASTSAFETENSORS_NOGDS=1 → FASTSAFETENSORS_CONFIG_JSON 的兼容覆写发生在之后的 database.py:337-344。预检读到的是覆写前配置,与实际构造 AutoLoader 时生效的 base/nogds 配置不一致;若上游 load_config() 做进程级缓存,database.py 的覆写还可能对同进程后续加载(ViT / draft / EPLB)完全失效。两种 copier 的 peak 若不同,AUTO 可能基于错误预算选中 fastsafetensors 而在加载期 OOM,或过度保守退回 scratch。该不一致只在 AUTO 出现,显式 LOAD_METHOD=fastsafetensors 不走预检,行为不统一。
建议: 把 FASTSAFETENSORS_NOGDS → FASTSAFETENSORS_CONFIG_JSON 的归一化提前到任何 load_config() 调用之前(例如在加载入口一次性初始化),保证预检与实际构造读到同一份配置;或让 _fastsafetensors_transient_budget_bytes 显式接受最终生效的配置对象,消除对调用顺序的隐式依赖。同时确认上游 load_config() 是否做进程级缓存并在注释中记录结论。
| device, | ||
| True, | ||
| stacked_key_config=stacked_key_config, | ||
| local_copyout_filter=required_checkpoint_keys.__contains__, |
There was a problem hiding this comment.
[P2] copyout 谓词跨 rank 不对称,EP>1 语义边界未确认且无多卡覆盖
required_checkpoint_keys 来自 tensor_to_weight_map(loader.py:578-627),其 MoE 部分经 MoeAtomicWeight.get_tensor_names(ffn_weight.py:706-720)→ LoadConfig.get_selected_experts(load_config.py:89-98)按 ep_rank 切片,故 ep_size>1 时非 stacked 的 per-expert key 集合随 rank 不同(stacked 原始 key 对称)。该非对称谓词被无条件传入以进程组为基础的 AutoLoader,而 torch_patch.py:33-38 明确记载 AutoLoader 在多 rank 加载时通过 pg.broadcast 搬运权重,即内部确有跨 rank 集合通信。若上游把 copyout 判定与集合调度绑定,可能出现广播缺参与方而 hang。新增用例全部为单 rank + fake loader,无任何 ep_size>1 覆盖。
建议: 先确认并在 loader.py:467 调用处注释中写明上游对 rank 间不一致 local_copyout_filter 的语义边界(是否仅作用于本 rank 的 copy-out、不参与集合通信调度),并引用上游文档与版本;若语义要求对称,改为传入各 rank key 的并集。补一条 ep_size > 1 的多进程单测或 smoke,验证非本 rank expert 被过滤后权重仍完整、无 hang——这是本次唯一真正改变多卡加载行为的分支,却完全没有多卡覆盖。
Checklist: [6.1] 错误语义:fail-fast/retry/fallback/silent 行为显式;[6.1] 分布式/跨平台变更有对应覆盖
LLLLKKKK
left a comment
There was a problem hiding this comment.
AI Code Review - PR #1354
Status: BLOCKING
Summary: P0/0 · P1/2 · P2/20 · P3/7
Reviewed: commit aa88afea4811 · 2026-08-31 01:26 UTC+8
Blocking Issues
P1
- cuda13_arm wheel 分发的 flashinfer 变体与实际链接的变体不一致 @
rtp_llm/libs/BUILD:78- 建议:先明确 cuda13_arm 的 flashinfer 归属再统一两侧条件:若 ARM 也应使用 cu13 变体,请在
flashinfer_deps()补using_cuda13_arm分支使链接与打包同源(ARM 在 CUDA 13 下能否用不带 cub 兼容补丁的默认变体本身也需验证);若不应使用,则把whl_package_libs的using_cuda13_arm分支改指默认仓库产物或移除。建议同时为每个共享库建native.alias+select(),让链接与打包共用同一路由,避免再次漂移,并在 PR 描述中附一次 cuda13_arm wheel 的构建与加载验证。
- 建议:先明确 cuda13_arm 的 flashinfer 归属再统一两侧条件:若 ARM 也应使用 cu13 变体,请在
- cuda12_9 公开依赖集引入内网制品库直链,破坏公网可构建性 @
deps/requirements_torch_gpu_cuda12_9.txt:5- 建议:按该文件既有约定,把这两个 wheel 转存到其余依赖所用的公开对象存储后再改 pin,并优先发布去掉
.dev的正式版本;若必须保留内网来源,请在 cu129 的 requirements/lock 中显式声明该 index 与 trusted-host,在文件注释与 PR 描述中标注该配置不再支持公网构建、给出回退到旧公开 wheel 的方式,并说明制品保留策略(三平台 lock 同时依赖同一批快照,被回收将一起失效)。
- 建议:按该文件既有约定,把这两个 wheel 转存到其余依赖所用的公开对象存储后再改 pin,并优先发布去掉
Non-blocking Suggestions
P2
- copyout 谓词跨 rank 不对称,EP>1 下与集合通信语义冲突且零覆盖 @
rtp_llm/model_loader/loader.py:465- 建议:三项任一即可解除阻塞:一是补
ep_size=2的 fake-pg 用例或 stacked-MoE smoke 目标,断言各 rank 广播次数一致、权重逐元素相同且不挂起;二是在代码注释与 PR 描述中固化 wheel 契约——local_copyout_filter仅作用于 rank 本地 copy-out、不参与任何集合通信的成组决策,并附一次 EP>1 实际加载记录;三是改为下传跨 rank 求并集后的 rank 无关 key 集合,EP 选择仍由loader.py:497的tensor_to_weight_map过滤完成,从源头消除分歧。
- 建议:三项任一即可解除阻塞:一是补
- 删除唯一版本闸门后无能力探测,旧 wheel 环境由可降级变为硬失败 @
rtp_llm/model_loader/loader.py:267- 建议:把 AUTO 判定顺序调整为先
has_module(即可消除噪声告警),并把存在性判断收紧为能力判断(探测AutoLoader属性,或用inspect.signature校验dim0_split_templates/local_copyout_filter),探测失败降级 SCRATCH 并 warning;若坚持 fail-fast,则在导入处给出带最低版本号与升级指引的错误信息以替代被删的版本护栏,并把两个定制 kwarg 用同一套except TypeError转译覆盖。同时在文档补一行最低 wheel 能力要求与旧环境处置方式(升级镜像或显式--load_method scratch)。
- 建议:把 AUTO 判定顺序调整为先
- estimated_peak_device_bytes 缺正值下界,异常上报会使显存闸门失效 @
rtp_llm/model_loader/loader.py:359- 建议:要求正整数才采用,例如
if not isinstance(estimate, int) or estimate <= 0: return legacy_budget,或加下界max(estimate, max_file_size),回退时打日志说明原因;并在TestFastsafetensorsTransientBudget补 0 与负值两个参数化用例,锁定"异常上报不得绕过门禁"。
- 建议:要求正整数才采用,例如
- load_config() 早于 NOGDS 环境归一化被调用,预检预算与实际配置不一致 @
rtp_llm/model_loader/loader.py:356- 建议:把 NOGDS→
FASTSAFETENSORS_CONFIG_JSON的映射抽成幂等的配置准备函数(如apply_fastsafetensors_env_overrides())在两处复用,在读取load_config()之前先调用,保证选路与加载看到同一份配置;或至少把当前FASTSAFETENSORS_CONFIG_JSON摘要打进预检日志便于事后核对。
- 建议:把 NOGDS→
- full-stacked 模式的额外显存峰值未纳入准入预算 @
rtp_llm/utils/database.py:387- 建议:在 full-stacked 模式下按最大 stacked tensor 尺寸放大 transient 预算(或至少乘一个模式相关系数),使准入检查与实际交付方式一致;若暂不改代码,请在文档中明确该模式仅建议在显存充裕的对比实验中使用,并给出配套动作(改用
--load_method scratch对照或预留更大运行时显存)。
- 建议:在 full-stacked 模式下按最大 stacked tensor 尺寸放大 transient 预算(或至少乘一个模式相关系数),使准入检查与实际交付方式一致;若暂不改代码,请在文档中明确该模式仅建议在显存充裕的对比实验中使用,并给出配套动作(改用
- 模式环境变量为空串时抛异常导致加载硬失败 @
rtp_llm/model_loader/loader.py:410- 建议:令空值等价于未设置,例如
mode = (os.environ.get("RTP_FASTSAFETENSORS_STACKED_MOE_MODE") or "").strip() or "per-expert",仅对非空且不在白名单内的取值 fail-fast;并补空串与仅含空白两个边界用例。
- 建议:令空值等价于未设置,例如
- 新开关绕过 LoadConfig 配置面,取值集合三处重复且不入启动配置快照 @
rtp_llm/model_loader/loader.py:413- 建议:按既有约定把开关落到
LoadConfig(字段 +to_string()+--flag,env 名保持不变以兼容脚本),并定义class StackedMoeMode(str, Enum)与LoadMethod并列,由配置层单点完成 env→枚举解析与校验,loader.py/database.py只消费已校验值;同时按表格行把该开关与FASTSAFETENSORS_NOGDS登记进文档 Load Config 表,并注明非法取值在权重加载阶段以 ValueError 直接失败、取值大小写敏感。
- 建议:按既有约定把开关落到
- 平台无关宏无条件引用 CUDA-13 专用仓库,非 cuda13 平台通配构建会被动拉取 @
arch_config/arch_select.bzl:28- 建议:把目标定义与消费端一起按配置收敛:两个 flashinfer 仓库的
sub_lib目标同名、输出文件名一致,可先为每个共享库建native.alias,actual用select()在 cuda13 下指向@flashinfer_cpp_cu13//:<t>、默认指向@flashinfer_cpp//:<t>,再对 alias 调用copy_so(whl_package_libs引用的目标名无需改动);或把该循环下沉为独立 macro / 加显式开关,仅由 cuda13 配置调用。该方案同时消除上文 ARM 打包与链接不同源的问题。
- 建议:把目标定义与消费端一起按配置收敛:两个 flashinfer 仓库的
- 11 个 flashinfer 目标名多处手抄,无单一来源且漏发不可检测 @
arch_config/arch_select.bzl:16- 建议:在
arch_config/arch_select.bzl导出唯一清单常量(如CUDA13_FLASHINFER_SO_TARGETS),copy_all_so()与rtp_llm/libs/BUILD都从该常量派生(BUILD 侧用列表推导);或把这些目标声明直接下沉到rtp_llm/libs/BUILD。合并前确认内部@arch_config已同步导出同名常量,并补一条最小 cuda13 校验(构建后断言 11 个.so均落入 wheel,或加一步从构建产物导入引擎的冒烟),使清单不一致在 CI 而非部署时暴露。
- 建议:在
- 同一 fastsafetensors x86 wheel 跨 torch 2.8 与 2.11 复用,与注释声明矛盾 @
deps/requirements_lock_torch_gpu_cuda12_9.txt:768- 建议:确认该 wheel 是否含 torch ABI 绑定的原生扩展:若含,为 cu129 与 cu130 分别出包并分别 pin;若确实与 torch ABI 无关(原生部分都在
fast_safetensors中),把注释改为"仅 fast_safetensors 按 torch 版本重建,fastsafetensors 为 torch 无关、仅区分 CPU 架构的构建,cu129/cu130 共用同一制品",并删掉重复注释,避免后续维护者按"每个 torch 版本必须重编"的错误前提操作。
- 建议:确认该 wheel 是否含 torch ABI 绑定的原生扩展:若含,为 cu129 与 cu130 分别出包并分别 pin;若确实与 torch ABI 无关(原生部分都在
- copyout filter 用例未隔离模式环境变量,文档给出的场景会直接使其失败 @
rtp_llm/model_loader/test/test_stacked_moe_weight.py:315- 建议:给该用例加
patch.dict("os.environ", {...}, clear=False)并显式注入"per-expert"(或 pop 该变量、直接桩掉_fastsafetensors_stacked_moe_mode);把"未设置时默认 per-expert"的契约拆成独立用例单独固定,不要压在这个宽口径用例里。
- 建议:给该用例加
- 缺少对 clone 语义的断言,去掉 clone 后测试仍会通过 @
rtp_llm/utils/test/ckpt_database_test.py:273- 建议:在该用例中保留源张量引用并断言切片与源不共享存储(例如比较
untyped_storage().data_ptr()不同),使.clone()被移除时必然测试失败。
- 建议:在该用例中保留源张量引用并断言切片与源不共享存储(例如比较
- per-expert 拆分用例由 fake 自行完成拆分,用例名与实际覆盖不符 @
rtp_llm/utils/test/ckpt_database_test.py:119- 建议:将用例改名为如实描述覆盖范围(例如
test_dim0_split_templates_are_forwarded_and_prewired_keys_pass_through);另补一个用例让 fake 吐出原始 stacked 键与真实张量,断言 per-expert 模式下生产不会二次拆分,把"恰好拆分一次"这一不变量固定下来。
- 建议:将用例改名为如实描述覆盖范围(例如
- 旧 wheel 的 ImportError 回退分支零覆盖,与 docstring 承诺不符 @
rtp_llm/model_loader/test/test_stacked_moe_weight.py:401- 建议:补两条用例:其一让 fake 模块不定义
load_config(或使导入抛ImportError),断言返回3 * max_file_size;其二让load_config()抛ValueError,断言同样回退且用assertLogs确认 warning 已发出,保证配置错误不会被无声吞掉;同时补max_file_size=0边界。
- 建议:补两条用例:其一让 fake 模块不定义
- fastsafetensors 生产边界被手写 fake 完全替代,wheel 契约无 PR 门禁校验 @
rtp_llm/utils/test/ckpt_database_test.py:127- 建议:
rtp_llm/models_py/standalone/BUILD:4已通过requirement(["fast-safetensors", "fastsafetensors"])在该 package 声明了真实 wheel 的别名目标,可直接补一条契约用例并依赖它:用inspect.signature(AutoLoader.__init__)断言其接受local_copyout_filter与dim0_split_templates,并确认该用例进入 cuda13 lane 的 PR 门禁;同时在 PR 描述中给出真实 wheel 下 per-expert 与 full-stacked 两种模式的峰值显存与权重一致性对比记录。
- 建议:
- 6 份重复的 fake 脚手架与两种打桩写法并存 @
rtp_llm/utils/test/ckpt_database_test.py:90- 建议:抽出模块级
_install_fake_fastsafetensors(auto_loader_cls)工厂与共享FakeSingleGroup,各用例只传差异化的FakeAutoLoader;并统一改用patch.dict管理sys.modules与os.environ(异常安全且与仓库其余测试一致),去掉手工存取。
- 建议:抽出模块级
- 文档把 full-stacked 写成仅供性能对比,实为 wheel 能力不足时的唯一回滚手段 @
docs/backend/server_arguments.md:229- 建议:补充说明 per-expert 默认路径要求所装 fastsafetensors 支持
dim0_split_templates(给出最低 wheel 版本/能力要求),否则加载以 RuntimeError 失败;把该句改为"用于受控性能对比,以及所装 fastsafetensors 不支持有界显存投递时的官方回滚开关",措辞与database.py:368-373的错误提示保持一致。
- 建议:补充说明 per-expert 默认路径要求所装 fastsafetensors 支持
- 文档 continue to use 措辞掩盖了两处默认行为变更 @
docs/backend/server_arguments.md:237- 建议:改写该段并补迁移说明:区分"库侧可配项"(bucket/queue/copier 现完全交由 fastsafetensors 配置入口,RTP 不再强制取值,并给出恢复旧 bucket 大小的 JSON 键名与库默认值)与"RTP 侧固定行为"(rank-local copyout 过滤本版本默认启用、按本 rank 所需 checkpoint key 推导、无 env 可关闭);把逐 expert 广播的描述标注为所依赖 wheel 的行为而非 RTP 保证,并去掉暗示行为未变的
continue to use措辞。
- 建议:改写该段并补迁移说明:区分"库侧可配项"(bucket/queue/copier 现完全交由 fastsafetensors 配置入口,RTP 不再强制取值,并给出恢复旧 bucket 大小的 JSON 键名与库默认值)与"RTP 侧固定行为"(rank-local copyout 过滤本版本默认启用、按本 rank 所需 checkpoint key 推导、无 env 可关闭);把逐 expert 广播的描述标注为所依赖 wheel 的行为而非 RTP 保证,并去掉暗示行为未变的
- 文档把生效前提写成显式 LOAD_METHOD,遗漏默认 auto 同样走 AutoLoader @
docs/backend/server_arguments.md:208- 建议:把适用条件改为"当实际选中 fastsafetensors 装载路径时(显式
LOAD_METHOD=fastsafetensors,或默认auto自动选中)",并补一句 auto 的选中条件(safetensors 权重 + 非 CPU convert device + 显存足够 + 模块可用),避免运维误判这些开关与自己无关。
- 建议:把适用条件改为"当实际选中 fastsafetensors 装载路径时(显式
- NOGDS 兼容开关的优先级结论未验证,且覆写不可逆污染进程级 env @
docs/backend/server_arguments.md:223- 建议:去掉
therefore的推导式表述,改为写明 RTP 只保证覆写FASTSAFETENSORS_CONFIG_JSON、同时设置文件路径时的最终生效结果由库决定(并标注所依赖的 wheel 版本);补一句该覆写是进程级不可恢复写入、会覆盖用户自设 inline JSON,并提示 auto 模式预检读取的是覆写前配置;建议为"FASTSAFETENSORS_CONFIG文件路径 + NOGDS"组合补一个用例。
- 建议:去掉
P3
- use_tqdm_on_load 成为死参数,加载进度可观测性静默丢失 @
rtp_llm/utils/database.py:312- 建议:要么删除该参数并同步更新调用方与测试,要么在接口 docstring 中注明其已被 fastsafetensors 配置接管、保留仅为兼容。
- 预算 helper 的 docstring 字段名与实现不符,且异常捕获过宽 @
rtp_llm/model_loader/loader.py:352- 建议:修正 docstring 字段名为
estimated_peak_device_bytes;去掉冗余的ModuleNotFoundError与AttributeError;对配置解析类错误改为直接抛出并提示 env 名,仅对"wheel 版本过旧"保留回退,并在注释中记录所依赖的 wheel 最低能力,替代已删除的版本闸门语义。
- 建议:修正 docstring 字段名为
- 新增纯逻辑用例被 H20 GPU 目标门禁,文件头 Covers 清单与归类未更新 @
rtp_llm/model_loader/test/test_stacked_moe_weight.py:302- 建议:把无 GPU 依赖的用例拆到不带
H20tag 与exec_properties的独立py_test目标,使其在所有平台 lane 都执行;同时把交付模式/copyout 用例移到独立测试类(如TestFastsafetensorsDelivery)并补全文件头Covers:清单。
- 建议:把无 GPU 依赖的用例拆到不带
- select 并列分支未复用仓库既有 config_setting_group 约定 @
rtp_llm/libs/BUILD:76- 建议:在根
BUILD增加selects.config_setting_group(name = "using_cuda13", match_any = [":using_cuda13_x86", ":using_cuda13_arm"]),本处 select 收敛为单分支,与仓库既有惯例保持一致。
- 建议:在根
- dev 快照 wheel 缺少构建来源追溯,lock 生成方式待确认 @
deps/requirements_torch_gpu_cuda13.txt:30- 建议:在注释中补充这两个 wheel 的上游仓库、分支与 commit(含 fuse-shm 特性来源),保持与同文件其它自建 wheel 一致的追溯规范,并说明 dev 制品的保留策略与回滚方式(保留旧 URL 作注释);同时在 PR 描述中附三个 lock 的生成命令,若确为重新生成且依赖图无变化请注明。
- cuda13 wheel 元数据 pin 分散三处无同步提示,且缺 cuda13_arm 分支 @
arch_config/arch_select.bzl:104- 建议:在 cuda13_x86 分支上方补一条与 ROCm 分支同风格的同步注释;并确认 cuda13_arm 的 wheel 元数据是否也应携带这些 pin,如是请在同一 PR 补齐该分支,避免 ARM wheel 的 install_requires 与实际安装制品长期不一致。
- CUDA13 打包变更与本 PR 主题无关,缺少动机说明与构建证据 @
arch_config/arch_select.bzl:15- 建议:建议把 CUDA13 打包变更拆为独立提交或独立 PR,或在 PR 描述中补充动机、影响平台清单与一次 cuda13 x86/ARM 的 wheel 构建与加载验证记录,使打包变更的风险可独立评估与回滚。
Checklist Findings (22 fail / 48 total)
General Principles Checklist
- [6.1] Architecture — 依赖方向:无循环依赖/跨层惊喜 → issue
平台无关宏无条件引用 CUDA-13 专用仓库,非 cuda13 平台通配构建会被动拉取
copy_all_so()第 15-28 行硬编码copy_so("@flashinfer_cpp_cu13//:" + target),而同文件flashinfer_deps()(:164-171)只在 cuda13_x86 使用该仓库;原有 4 个copy_so全是全平台可用的@rtp_llm//目标。rtp_llm/libs/BUILD:6无条件调用该宏,因此 cuda12 / cuda12_9 / rocm / cpu / arm 配置下也会在该包生成 11 个 genrule,其源是需 nvcc 编译的 CUDA13 专属cc_shared_library(deps/git.bzl:88-109独立 git 仓库)。消费端 select 已按配置收敛、定义端没有:任何通配构建或查询(//rtp_llm/libs:all、//rtp_llm/...、compdb 刷新)都会拉取并编译 CUDA13 flashinfer,在 ROCm/CPU 上因无 nvcc 直接失败,在 cuda12 上是一次完全冗余的重编译。 - [6.1] Architecture — 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全 → issue
cuda13 wheel 元数据 pin 分散三处无同步提示,且缺 cuda13_arm 分支
_同一组 wheel URL 现需人工保持三处一致:whl_deps()的 cuda13_x86 分支(:104-105)、deps/requirements_torch_gpu_cuda13.txt:34-35、对应 lock(:622、:629);同函数的 ROCm 分支在 :111-112 专门写了"Keep the ROCm AITER/FlyDSL pins synchronized with deps/requirements{,_lock}rocm.txt"的同步提示,新增的 cuda13 pin 没有。此外whl_deps()没有using_cuda13_arm分支(该配置按根 BUILD:44-59 是using_cuda12的严格特化,会落到torch==2.6.0+cu126分支),而requirements_cuda13_arm.txt:19-20已加这两个 pin,两侧口径不一致。 - [6.1] Architecture — 分层边界:新概念在正确层级,不泄漏内部 → issue
平台无关宏无条件引用 CUDA-13 专用仓库,非 cuda13 平台通配构建会被动拉取
copy_all_so()第 15-28 行硬编码copy_so("@flashinfer_cpp_cu13//:" + target),而同文件flashinfer_deps()(:164-171)只在 cuda13_x86 使用该仓库;原有 4 个copy_so全是全平台可用的@rtp_llm//目标。rtp_llm/libs/BUILD:6无条件调用该宏,因此 cuda12 / cuda12_9 / rocm / cpu / arm 配置下也会在该包生成 11 个 genrule,其源是需 nvcc 编译的 CUDA13 专属cc_shared_library(deps/git.bzl:88-109独立 git 仓库)。消费端 select 已按配置收敛、定义端没有:任何通配构建或查询(//rtp_llm/libs:all、//rtp_llm/...、compdb 刷新)都会拉取并编译 CUDA13 flashinfer,在 ROCm/CPU 上因无 nvcc 直接失败,在 cuda12 上是一次完全冗余的重编译。 - [6.1] Architecture — 可观测性:日志/指标/超时可操作、非噪声 → issue
预算 helper 的 docstring 字段名与实现不符,且异常捕获过宽
docstring 写"当max_batch_bytesis unset 时保留旧估算"(:352),实现读取的却是estimated_peak_device_bytes(:359),字段名不一致会误导后续维护者对 wheel 契约的理解。捕获列表(ImportError, ModuleNotFoundError, AttributeError, ValueError)中ModuleNotFoundError是ImportError子类属冗余,AttributeError与getattr(..., None)重复;ValueError还会连带吞掉json.JSONDecodeError,即FASTSAFETENSORS_CONFIG_JSON写错时探测阶段只打 warning,真正报错要等到AutoLoader构造,排障链路被拉长。 - [6.1] Architecture — 回滚路径:风险行为存在运维回滚手段 → issue
文档 continue to use 措辞掩盖了两处默认行为变更
文档称普通张量 "continue to use the FastSafeTensors bucket and rank-local-copy settings",continue读作行为未变且均为既有可配项。实际两处变更:一是database.py:352-363现只传 pg/files/device 与local_copyout_filter,全仓已搜不到bbuf_size_kb,旧实现由 RTP 强制的 batch buffer 大小与 shm copier 均被移除,回落到库默认或FASTSAFETENSORS_CONFIG_JSON,存量部署升级后装载耗时与峰值显存会静默改变;二是 rank-local copyout 过滤为本次新增,由 loader.py:490 无条件传入且无开关可关。另第 227-229 行描述的"source rank 先切片、各 rank 逐 expert 广播"已不再是 RTP 实现,而是对 wheel 内部行为的断言。本页对--worker_info_port_num与 Removed FMHA opti - [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue
NOGDS 兼容开关的优先级结论未验证,且覆写不可逆污染进程级 env
文档先断言 inline JSON 优先于文件路径,再据此推出 NOGDS "therefore takes priority over other fastsafetensors configuration"。但 RTP 只覆写FASTSAFETENSORS_CONFIG_JSON(database.py:344-347),从不触碰FASTSAFETENSORS_CONFIG文件路径,该优先级完全由第三方库决定,本仓无实现也无测试佐证(ckpt_database_test.py:352-353只覆盖 CONFIG_JSON + NOGDS 组合)。文档也未说明该覆写是对os.environ的就地不可恢复写入(用例 :359-362 明确断言调用后仍是覆盖值),用户自设 inline JSON 被静默丢弃(仅一条 warning),且该覆写晚于 auto 模式的显存预检。 - [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue
NOGDS 兼容开关的优先级结论未验证,且覆写不可逆污染进程级 env
文档先断言 inline JSON 优先于文件路径,再据此推出 NOGDS "therefore takes priority over other fastsafetensors configuration"。但 RTP 只覆写FASTSAFETENSORS_CONFIG_JSON(database.py:344-347),从不触碰FASTSAFETENSORS_CONFIG文件路径,该优先级完全由第三方库决定,本仓无实现也无测试佐证(ckpt_database_test.py:352-353只覆盖 CONFIG_JSON + NOGDS 组合)。文档也未说明该覆写是对os.environ的就地不可恢复写入(用例 :359-362 明确断言调用后仍是覆盖值),用户自设 inline JSON 被静默丢弃(仅一条 warning),且该覆写晚于 auto 模式的显存预检。 - [6.1] Quality — Commit 原子、message 与行为匹配 → issue
CUDA13 打包变更与本 PR 主题无关,缺少动机说明与构建证据
本 PR 主题是 fastsafetensors 加载路径下沉与 wheel 升级,但同时在copy_all_so()新增 11 个 CUDA13 flashinfer 共享库复制目标并改动rtp_llm/libs/BUILD的打包 select,两者无逻辑耦合,无法独立回滚或 bisect。该改动引入了上文两个跨平台一致性问题(无条件注入 cu13 仓库、ARM 变体不一致),却没有配套的构建/加载验证证据,动机(补齐 wheel 中缺失的libflashinfer_*.so)需读者自行推断。 - [6.1] Quality — Mega-PR 已拆分为独立变更 → issue
CUDA13 打包变更与本 PR 主题无关,缺少动机说明与构建证据
本 PR 主题是 fastsafetensors 加载路径下沉与 wheel 升级,但同时在copy_all_so()新增 11 个 CUDA13 flashinfer 共享库复制目标并改动rtp_llm/libs/BUILD的打包 select,两者无逻辑耦合,无法独立回滚或 bisect。该改动引入了上文两个跨平台一致性问题(无条件注入 cu13 仓库、ARM 变体不一致),却没有配套的构建/加载验证证据,动机(补齐 wheel 中缺失的libflashinfer_*.so)需读者自行推断。 - [6.1] Quality — PR description 说明动机与设计 → issue
CUDA13 打包变更与本 PR 主题无关,缺少动机说明与构建证据
本 PR 主题是 fastsafetensors 加载路径下沉与 wheel 升级,但同时在copy_all_so()新增 11 个 CUDA13 flashinfer 共享库复制目标并改动rtp_llm/libs/BUILD的打包 select,两者无逻辑耦合,无法独立回滚或 bisect。该改动引入了上文两个跨平台一致性问题(无条件注入 cu13 仓库、ARM 变体不一致),却没有配套的构建/加载验证证据,动机(补齐 wheel 中缺失的libflashinfer_*.so)需读者自行推断。 - [6.1] Quality — 逻辑变更未混入无关格式化 → issue
新增纯逻辑用例被 H20 GPU 目标门禁,文件头 Covers 清单与归类未更新
新增的 copyout keys / stacked mode / transient budget 三组用例都是纯 Python 逻辑、不需要 GPU,但全部挂在rtp_llm/model_loader/test/BUILD:5-13的test_stacked_moe_weight目标下,该目标带tags = ["H20"]与exec_properties = {'gpu': 'H20'},而本 PR 的目标平台是 cuda13 x86/ARM 与 cu129;同 BUILD 的test_per_channel_fp8_helpers(:34-38)已示范无 GPU 门禁的写法。此外这些交付路径用例被塞进TestBuildStackedKeyConfig(其 docstring 声明只测_build_stacked_key_config),文件头Covers:清单(:3-6)仍只列旧的三项。 - [6.1] Software Engineering — DRY:重复非平凡逻辑被抽取或显式复用 → issue
cuda13 wheel 元数据 pin 分散三处无同步提示,且缺 cuda13_arm 分支
_同一组 wheel URL 现需人工保持三处一致:whl_deps()的 cuda13_x86 分支(:104-105)、deps/requirements_torch_gpu_cuda13.txt:34-35、对应 lock(:622、:629);同函数的 ROCm 分支在 :111-112 专门写了"Keep the ROCm AITER/FlyDSL pins synchronized with deps/requirements{,_lock}rocm.txt"的同步提示,新增的 cuda13 pin 没有。此外whl_deps()没有using_cuda13_arm分支(该配置按根 BUILD:44-59 是using_cuda12的严格特化,会落到torch==2.6.0+cu126分支),而requirements_cuda13_arm.txt:19-20已加这两个 pin,两侧口径不一致。 - [6.1] Software Engineering — KISS/YAGNI:无投机性抽象 → issue
use_tqdm_on_load 成为死参数,加载进度可观测性静默丢失
改造后fastsafetensors_weights_iterator的函数体已不再使用use_tqdm_on_load(进度显示随批次策略一并下沉给 wheel,:325-393 内部无任何引用,仅原样传给内层iterator),但loader.py:486-492仍按位置固定传True,形成无效果的参数契约,后续读者会误以为该参数仍能控制加载进度条。 - [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue
新增纯逻辑用例被 H20 GPU 目标门禁,文件头 Covers 清单与归类未更新
新增的 copyout keys / stacked mode / transient budget 三组用例都是纯 Python 逻辑、不需要 GPU,但全部挂在rtp_llm/model_loader/test/BUILD:5-13的test_stacked_moe_weight目标下,该目标带tags = ["H20"]与exec_properties = {'gpu': 'H20'},而本 PR 的目标平台是 cuda13 x86/ARM 与 cu129;同 BUILD 的test_per_channel_fp8_helpers(:34-38)已示范无 GPU 门禁的写法。此外这些交付路径用例被塞进TestBuildStackedKeyConfig(其 docstring 声明只测_build_stacked_key_config),文件头Covers:清单(:3-6)仍只列旧的三项。 - [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue
新增纯逻辑用例被 H20 GPU 目标门禁,文件头 Covers 清单与归类未更新
新增的 copyout keys / stacked mode / transient budget 三组用例都是纯 Python 逻辑、不需要 GPU,但全部挂在rtp_llm/model_loader/test/BUILD:5-13的test_stacked_moe_weight目标下,该目标带tags = ["H20"]与exec_properties = {'gpu': 'H20'},而本 PR 的目标平台是 cuda13 x86/ARM 与 cu129;同 BUILD 的test_per_channel_fp8_helpers(:34-38)已示范无 GPU 门禁的写法。此外这些交付路径用例被塞进TestBuildStackedKeyConfig(其 docstring 声明只测_build_stacked_key_config),文件头Covers:清单(:3-6)仍只列旧的三项。 - [6.1] Tests — 被删除测试有等价替代覆盖 → issue
fastsafetensors 生产边界被手写 fake 完全替代,wheel 契约无 PR 门禁校验
本 PR 把 fastsafetensors 跨大版本升级、API 从ParallelLoader改为AutoLoader,并新增local_copyout_filter、dim0_split_templates两个定制 kwarg 与load_config().estimated_peak_device_bytes字段。但ckpt_database_test.py:90起的 6 个用例全部用types.ModuleType伪造fastsafetensors注入sys.modules(:146/:205/:249/:281/:302/:347),test_stacked_moe_weight.py:344-355又把_create_model_weights/_generate_weight_info/_build_stacked_key_config/ database 整体 MagicMock。AutoLoader(...)签名与{expert_id}模板占位约定只在测试内自 - [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue
旧 wheel 的 ImportError 回退分支零覆盖,与 docstring 承诺不符
loader.py:347-366的 docstring 声称"Keep the historical three-shard estimate when loading an older wheel",其 except 捕获ImportError/ModuleNotFoundError/AttributeError/ValueError并回退3 * max_file_size。但新增两个用例(:388、:401)都提供可用的load_config,只区分estimated_peak_device_bytes有值与None,即只覆盖 try 分支。"旧 wheel 没有load_config"这一 docstring 明确承诺、也是最主要的兼容场景完全无用例;该返回值经 loader.py:335-344 直接决定是否放行 fastsafetensors 路径,且该 except 会把FASTSAFETENSORS_CONFIG_JSON配置错误(JSONDecodeError属ValueError)静默降级为 lega
RTP-LLM Checklist
- [I] 代码质量 — 删除或重命名内部 file、registry entry、model name、metric enum、op binding、plugin symbol 时,必须全仓搜索消费者,并提供替代实现、迁移说明或 smoke 覆盖;只有暴露到 HTTP/RPC/config/persisted format 时才按外部兼容性处理 → issue
fastsafetensors 生产边界被手写 fake 完全替代,wheel 契约无 PR 门禁校验
本 PR 把 fastsafetensors 跨大版本升级、API 从ParallelLoader改为AutoLoader,并新增local_copyout_filter、dim0_split_templates两个定制 kwarg 与load_config().estimated_peak_device_bytes字段。但ckpt_database_test.py:90起的 6 个用例全部用types.ModuleType伪造fastsafetensors注入sys.modules(:146/:205/:249/:281/:302/:347),test_stacked_moe_weight.py:344-355又把_create_model_weights/_generate_weight_info/_build_stacked_key_config/ database 整体 MagicMock。AutoLoader(...)签名与{expert_id}模板占位约定只在测试内自 - [I] 代码质量 — 同一功能用统一工具函数 → issue
select 并列分支未复用仓库既有 config_setting_group 约定
新增 select 把同一个cuda13_flashinfer_shared_libs分别挂到//:using_cuda13_x86与//:using_cuda13_arm两个分支。根BUILD:108-124为完全相同的诉求建立了selects.config_setting_group(using_cuda12_9、using_cu12_9_or_13_x86),注释写明"Selects whose cuda13_x86 behavior is identical to cuda12_9_x86 use this group so we don't have to add a parallel branch to every select()"。后续若再有 cuda13 专属打包项,需在每个 select 重复维护两条分支。
Python Static-First Checklist
- [P.A] 静态结构与类型纪律 — 字符串分发用 Enum/Literal → issue
新开关绕过 LoadConfig 配置面,取值集合三处重复且不入启动配置快照
该开关直接读os.environ(:410),是loader.py唯一裸 env 读取;同类开关load_method/force_cpu_load_weights/loader_recycle_handles/moe_pure_tp_preshard均声明在py_config_modules.py:176-189的LoadConfig、有--flag并进入to_string()启动快照(docs/backend/server_arguments.md:201-204的表格即由此而来)。新开关无 flag、不出现在配置快照中,运维排障无法确认生效模式。字面量集合{"per-expert","full-stacked"}与报错文案在 loader.py:413 与 database.py:319-323 各写一遍,fastsafetensors_weights_iterator还带第三份字符串默认值(database.py:315);仓库已有LoadMethod枚举先例但未沿用。 - [P.B] 错误处理 — 禁止 bare except 或静默吞异常 → issue
预算 helper 的 docstring 字段名与实现不符,且异常捕获过宽
docstring 写"当max_batch_bytesis unset 时保留旧估算"(:352),实现读取的却是estimated_peak_device_bytes(:359),字段名不一致会误导后续维护者对 wheel 契约的理解。捕获列表(ImportError, ModuleNotFoundError, AttributeError, ValueError)中ModuleNotFoundError是ImportError子类属冗余,AttributeError与getattr(..., None)重复;ValueError还会连带吞掉json.JSONDecodeError,即FASTSAFETENSORS_CONFIG_JSON写错时探测阶段只打 warning,真正报错要等到AutoLoader构造,排障链路被拉长。 - [P.G] 测试规范 — mock/fake/stub 不得替代本次声称覆盖的生产边界 → issue
fastsafetensors 生产边界被手写 fake 完全替代,wheel 契约无 PR 门禁校验
本 PR 把 fastsafetensors 跨大版本升级、API 从ParallelLoader改为AutoLoader,并新增local_copyout_filter、dim0_split_templates两个定制 kwarg 与load_config().estimated_peak_device_bytes字段。但ckpt_database_test.py:90起的 6 个用例全部用types.ModuleType伪造fastsafetensors注入sys.modules(:146/:205/:249/:281/:302/:347),test_stacked_moe_weight.py:344-355又把_create_model_weights/_generate_weight_info/_build_stacked_key_config/ database 整体 MagicMock。AutoLoader(...)签名与{expert_id}模板占位约定只在测试内自
Strengths
- 用 wheel 的
dim0_split_templates取代自研ParallelLoader子类,不再依赖fb._get_rank_lidx/factory.tensors/free_dev_ptrs/fb.instantiated等大批私有属性,跨版本静默破裂面显著收窄。 - 删除收尾彻底:全仓搜索
PerExpertParallelLoader/per_expert_parallel_loader均零命中,rtp_llm/utils/torch_patch.py:33-39的 UE8M0 注释同步改写为AutoLoader,未留过期描述。 - 能力缺失走显式失败而非静默退化:
database.py:364-374把TypeError转成带操作指引的RuntimeError,并有test_per_expert_mode_fails_fast_when_wrapper_lacks_split_capability与test_wrapper_without_auto_loader_fails_instead_of_legacy_fallback两个用例真实驱动。 - 模式开关是"安全默认 + 显式 opt-in":默认
per-expert(有界显存),非法值 fail-fast 且报错含合法取值,配套用例用assertRaisesRegex带 match 断言防止错误信息退化。 _build_fastsafetensors_local_copyout_keys同时保留 raw stacked key 与展开后 per-expert key,使过滤在 wheel「split 前/后过滤」两种语义下都不漏 key,docstring 记录了原因。test_full_stacked_mode_disables_prebroadcast_split真实执行了 RTP 侧 dim0 拆分,既断言dim0_split_templates为None又校验切片键名与数值;ckpt_database_test.py:91-117完整保存并还原sys.modules与三个FASTSAFETENSORS_*变量(含"原本不存在"情形)。- 依赖与打包基础功课扎实:三平台 requirements 与 lock 的 URL/
--hash=sha256严格成对更新、whl_deps()无版本漂移;arch_select.bzl:16-27的 11 个目标与flashinfer_cu13.BUILD:133-143的sub_lib逐项吻合,rtp_llm/libs/BUILD:8-20抽成包级常量供两个 select 分支复用而非写两遍。
| len(stacked_key_config), | ||
| ) | ||
|
|
||
| required_checkpoint_keys = self._build_fastsafetensors_local_copyout_keys( |
There was a problem hiding this comment.
[P2] copyout 谓词跨 rank 不对称,EP>1 下与集合通信语义冲突且零覆盖
required_checkpoint_keys 由 tensor_to_weight_map 派生,其 MoE key 经 ffn_weight.py:706-720 → load_config.py:93-97 按 expert_per_ep * ep_rank 切片,故 EP>1 时各 rank 谓词集合必然不同;该谓词以 __contains__ 无条件下传(:490),而同时下传的 dim0_split_templates 是 rank 无关全量映射。本 PR 改写的 torch_patch.py:33-39 明确记载 AutoLoader 在多 rank 加载时通过 pg.broadcast 跨 rank 搬运权重;被删的 PerExpertParallelLoader 曾显式区分 pg.size()==1 与 broadcast 分支,该调度现全在 wheel 内。新增用例全为单 rank Fake(test_stacked_moe_weight.py:344-355),仓内无 EP>1 覆盖,也无 `--load_m...
建议: 三项任一即可解除阻塞:一是补 ep_size=2 的 fake-pg 用例或 stacked-MoE smoke 目标,断言各 rank 广播次数一致、权重逐元素相同且不挂起;二是在代码注释与 PR 描述中固化 wheel 契约——local_copyout_filter 仅作用于 rank 本地 copy-out、不参与任何集合通信的成组决策,并附一次 EP>1 实际加载记录;三是改为下传跨 rank 求并集后的 rank 无关 key 集合,EP 选择仍由 loader.py:497 的 tensor_to_weight_map 过滤完成,从源头消除分歧。
| @@ -332,12 +332,38 @@ def _is_memory_enough_for_fastsafetensor(self): | |||
| f"doubling model_mem estimate for fastsafetensor memory check" | |||
There was a problem hiding this comment.
📍 实际位置 rtp_llm/model_loader/loader.py:267(不在 diff 展示范围内,就近挂载)
[P2] 删除唯一版本闸门后无能力探测,旧 wheel 环境由可降级变为硬失败
被删文件是仓内唯一显式版本校验点(_REQUIRED_FST_VERSION = "0.1.19"),删除后全仓已搜不到任何版本/能力门禁。AUTO 仅以 has_module("fastsafetensors") 判断(:267),随后 from fastsafetensors import AutoLoader(database.py:317)对旧 wheel 直接 ImportError,即装旧 0.1.x wheel 的既有环境不再降级 SCRATCH 而是加载阶段崩溃(ckpt_database_test.py:288 已把该硬失败固化为预期)。local_copyout_filter 更无任何兜底:database.py:352-374 的友好转译只匹配 dim0_split_templates,wheel 不认识该 kwarg 时抛裸 TypeError,且无 env 可关闭。同时 _is_memory_enough_for_fastsafetensor() 排在 has_module 之前,未装 wheel 的平台每次加载都...
建议: 把 AUTO 判定顺序调整为先 has_module(即可消除噪声告警),并把存在性判断收紧为能力判断(探测 AutoLoader 属性,或用 inspect.signature 校验 dim0_split_templates / local_copyout_filter),探测失败降级 SCRATCH 并 warning;若坚持 fail-fast,则在导入处给出带最低版本号与升级指引的错误信息以替代被删的版本护栏,并把两个定制 kwarg 用同一套 except TypeError 转译覆盖。同时在文档补一行最低 wheel 能力要求与旧环境处置方式(升级镜像或显式 --load_method scratch)。
| from fastsafetensors import load_config | ||
|
|
||
| config = load_config() | ||
| estimate = getattr(config, "estimated_peak_device_bytes", None) |
There was a problem hiding this comment.
[P2] estimated_peak_device_bytes 缺正值下界,异常上报会使显存闸门失效
_fastsafetensors_transient_budget_bytes 只判断 estimate is None(:359-360),其余取值一律直接返回。该返回值是唯一门槛(enough = (free_mem - model_mem) > transient_mem,:338),若 wheel 对未配置项上报 0 或异常小值,闸门退化为 (free_mem - model_mem) > 0,等于取消改动前 3 * max_file_size 的 OOM 保护,AUTO 会在显存明显不足时仍选 fastsafetensors;max_file_mem 已降级为纯日志字段。测试只覆盖 8*1024 与 None(test_stacked_moe_weight.py:388-412),未覆盖 0/负值边界。
建议: 要求正整数才采用,例如 if not isinstance(estimate, int) or estimate <= 0: return legacy_budget,或加下界 max(estimate, max_file_size),回退时打日志说明原因;并在 TestFastsafetensorsTransientBudget 补 0 与负值两个参数化用例,锁定"异常上报不得绕过门禁"。
| """ | ||
| legacy_budget = 3 * max_file_size | ||
| try: | ||
| from fastsafetensors import load_config |
There was a problem hiding this comment.
[P2] load_config() 早于 NOGDS 环境归一化被调用,预检预算与实际配置不一致
AUTO 选路在 _is_memory_enough_for_fastsafetensor 内调用 load_config() 读取峰值估算(loader.py:335、355-360)。但 FASTSAFETENSORS_NOGDS=1 → FASTSAFETENSORS_CONFIG_JSON 的强制覆写发生在其后的 database.py:344-351(构造 AutoLoader 之前),且 ckpt_database_test.py:359-362 断言该覆写不可恢复地留在进程环境中。即选路时读到的是覆写前配置,实际加载用的是 base/nogds 配置;两者 batch buffer 记账不同时,闸门会基于错误预算放行或误判,前者带来加载期 OOM 风险。
建议: 把 NOGDS→FASTSAFETENSORS_CONFIG_JSON 的映射抽成幂等的配置准备函数(如 apply_fastsafetensors_env_overrides())在两处复用,在读取 load_config() 之前先调用,保证选路与加载看到同一份配置;或至少把当前 FASTSAFETENSORS_CONFIG_JSON 摘要打进预检日志便于事后核对。
LGTM:阻断已解除(rtpcli 自动清除旧红标)
| len(stacked_key_config), | ||
| ) | ||
|
|
||
| required_checkpoint_keys = self._build_fastsafetensors_local_copyout_keys( |
There was a problem hiding this comment.
[P2] copyout 谓词跨 rank 不对称,EP>1 下与集合通信语义冲突且零覆盖
loader.py:465 用 tensor_to_weight_map.keys() 构造过滤集,其 MoE key 来自 ffn_weight.py:706-720 的 get_tensor_names → load_config.py:89-98 的 get_selected_experts,后者按 expert_per_ep * ep_rank : expert_per_ep * (ep_rank + 1) 切片(开 EPLB 时还叠加 phy2log)。因此 ep_size>1 且 checkpoint 为非 stacked 的逐专家布局(stacked_ckpt_keys=False,如 Mixtral/Qwen-MoE)时,loader.py:490 传给 AutoLoader 的谓词在各 rank 上接受的真实 ckpt key 集合不同。被删的 _broadcast_per_expert 是对 range(num_experts) 全 rank 对称广播、由 RTP 侧再过滤;`torch_patch.py:3...
建议: 在 _build_fastsafetensors_local_copyout_keys 的 docstring 中显式写下该契约并给出 wheel 侧依据(当前参数名与注释暗示 rank 本地语义,但未被验证);同时补一个 ep_size>1 的多 rank 集成或 smoke 用例,实跑逐专家布局 MoE 的 fastsafetensors 加载。若短期无法验证,保守做法是在 ep_size > 1 时对 MoE key 取全 rank 并集(用 range(expert_num) 而非 get_selected_experts 结果)使谓词 rank 不变,仅保留非 MoE key 的过滤收益。
| @@ -332,12 +332,38 @@ def _is_memory_enough_for_fastsafetensor(self): | |||
| f"doubling model_mem estimate for fastsafetensor memory check" | |||
There was a problem hiding this comment.
📍 实际位置 rtp_llm/model_loader/loader.py:267(不在 diff 展示范围内,就近挂载)
[P2] 删除唯一版本闸门后无能力探测,旧 wheel 环境由可降级变为硬失败
被删实现有 _REQUIRED_FST_VERSION = "0.1.19" 显式守卫,版本不符时抛带升级指引的 RuntimeError;这是全仓唯一一处对 wheel 版本的主动断言。新实现无任何版本/能力校验:loader.py:262-269 的 AUTO 分支仅用 has_module("fastsafetensors")(utils/module_util.py:13 内部只做 find_spec)即选中 FASTSAFETENSORS,随后 database.py:317 执行 from fastsafetensors import AutoLoader, SingleGroup。旧 wheel 下抛裸 ImportError(ckpt_database_test.py:288 正是对此的断言),AUTO 路径无 SCRATCH 降级,服务启动直接失败;而 loader.py:348-353 的 docstring 仍声称支持「older wheel」,两处假设自相矛盾。文档新章节也未给出最低版本前提与回滚指引。
建议: 在 AUTO 选路阶段(has_module 之后)补一次能力探测:AutoLoader 不可导入时打告警并回落 LoadMethod.SCRATCH;显式 LOAD_METHOD=fastsafetensors 时抛出带最低 wheel 能力(需 AutoLoader/load_config(),per-expert 还需 dim0_split_templates)与升级指引的错误替代裸 ImportError。同时在 docs/backend/server_arguments.md 新章节开头补一句版本前提(以 deps/requirements_torch_gpu_*.txt 的 pin 为准)与回滚路径(LOAD_METHOD=scratch 或 full-stacked),并修正 docstring 中「兼容旧 wheel」的表述。
Checklist: [I] 删除或重命名内部 file、registry entry、model name、metric enum、op binding、plugin symbol 时,必须全仓搜索消费者,并提供替代实现、迁移说明或 smoke 覆盖;只有暴露到 HTTP/RPC/config/persisted format 时才按外部兼容性处理
| from fastsafetensors import load_config | ||
|
|
||
| config = load_config() | ||
| estimate = getattr(config, "estimated_peak_device_bytes", None) |
There was a problem hiding this comment.
[P2] estimated_peak_device_bytes 缺正值下界与类型校验,异常上报会使显存闸门失效
loader.py:360 的 return legacy_budget if estimate is None else estimate 只拦截 None。若 wheel 的 load_config() 把 estimated_peak_device_bytes 上报为 0(字段惰性计算未就绪或配置未解析时的默认零值),loader.py:335-338 得到 transient_mem=0.0,准入判据退化为 free_mem > model_mem,改造前恒定要求的 3 * max_file_size 余量被完全抹掉,AUTO 会在原先回退 SCRATCH 的紧显存机器上改选 fastsafetensors 并在加载期 OOM;负值同理更宽松。签名标注 -> int 但返回值直接来自外部对象属性,若为 str(JSON 配置常见)则 loader.py:335 的除法抛 TypeError,该异常发生在 try 块之外、无法回退 legacy 估算。
建议: 对 estimate 增加显式校验后再采用:仅当 isinstance(estimate, (int, float)) 且 estimate > 0 时使用,并对最终预算取下界 max(estimate, legacy_budget)(或至少 max_file_size),避免外部低报或异常值直接削弱门限;同时补 estimate=0、负值、非数值三个边界的单测。
| """ | ||
| legacy_budget = 3 * max_file_size | ||
| try: | ||
| from fastsafetensors import load_config |
There was a problem hiding this comment.
[P2] load_config() 早于 NOGDS 环境归一化被调用,预检预算与实际配置不一致
调用链顺序为 loader.py:266 先求值 _is_memory_enough_for_fastsafetensor() → :335 → :356-359 读 load_config().estimated_peak_device_bytes;而把 FASTSAFETENSORS_NOGDS=1 映射为 FASTSAFETENSORS_CONFIG_JSON={"loader":"base","base":{"copier_type":"nogds"}} 的覆写写在 database.py:344-351,位于含 yield 的 iterator 函数体内(database.py:395 的 return iterator(...) 仅创建生成器),首次 next() 才执行。于是门限按覆写前的用户 config(可能是 GDS/fuse-shm)估算,实际运行的却是 base/nogds loader,两者 batch buffer 与 device 峰值不同;显式 `LOAD_METHOD=fastsafetenso...
建议: 把 NOGDS → FASTSAFETENSORS_CONFIG_JSON 的兼容映射抽成两处复用的辅助函数(例如 _apply_fastsafetensors_env_compat()),并在任何 load_config() 调用之前统一归一化一次,保证门限与实际 loader config 同源;同时把最终生效的 loader 名与关键配置摘要打进 fastsafetensor memory check 日志,便于线上回溯选路依据。
05413c2 to
be48ee7
Compare
LLLLKKKK
left a comment
There was a problem hiding this comment.
AI Code Review - PR #1354
Status: BLOCKING
Summary: P0/0 · P1/1 · P2/18 · P3/4
Reviewed: commit be48ee7edaff · 2026-08-31 12:06 UTC+8
Blocking Issues
P1
- 硬依赖 fastsafetensors 新版 AutoLoader 能力但未对齐 wheel pin,删除版本闸门后 auto 路径无探测也无回退 @
rtp_llm/utils/database.py:317- 建议:在本 PR 内二选一并写入 PR description:(1)同步升级
deps/requirements*与requirements_lock_*到提供AutoLoader+local_copyout_filter+dim0_split_templates+estimated_peak_device_bytes的 wheel,并确认 cuda12_9 / cuda13 / cuda13_arm 三平台能力一致、写明最低版本与升级顺序;(2)暂不升级则补等价护栏:把 AUTO 判定从has_module收紧为正向能力探测(inspect.signature(AutoLoader.__init__)检查两个关键 kwarg,或校验版本下界),能力不足时降级为LoadMethod.SCRATCH并 warn,而非在权重加载中途抛裸 ImportError。同时在文档### FastSafeTensors loader configuration小节声明所需最低 wheel 版本。
- 建议:在本 PR 内二选一并写入 PR description:(1)同步升级
Non-blocking Suggestions
P2
- copyout 谓词跨 rank 不对称,EP>1 语义未固化且零覆盖 @
rtp_llm/model_loader/loader.py:465- 建议:在
_build_fastsafetensors_local_copyout_keys的 docstring 中显式声明该谓词允许跨 rank 分叉并给出上游语义依据;或在传入前跨 process group 校验键集合一致性(all_gather hash,不一致即 fail-fast 而非挂死)。补一个ep_size=2的用例(gloo + CPU 假 checkpoint 即可),断言两个ep_rank各自的required_checkpoint_keys恰好覆盖本 rank 所需 key 且 stacked raw key 在两 rank 上一致。另建议把loader.py:556的 fallback 日志从 info 升为 warning 或补一条 metric,使过滤误杀导致的静默回退可观测。
- 建议:在
- 能力错误映射只覆盖 dim0_split_templates,其余能力缺失无处置指引且静默降级不可观测 @
rtp_llm/utils/database.py:364- 建议:改为正向能力探测(显式检查
AutoLoader.__init__是否声明这两个 kwarg,或由 wheel 暴露能力标志)替代异常文本匹配,并让local_copyout_filter缺失时同样落入带指引的 RuntimeError;local_copyout_filter is None时不放入 kwargs,避免旧 wheel 在非 MoE 路径上无谓失败。另在per-expert模式下若迭代仍收到原始 stacked key,输出一次 warning(如「per-expert requested but loader delivered stacked key X; peak memory not bounded」)。测试侧补两条 TypeError 用例:函数体内抛TypeError("unrelated argument mismatch")断言原样上抛不被转成 RuntimeError;抛TypeError("dim0_split_templates must be a dict")把字符串嗅探的误判风险显式暴露出来。
- 建议:改为正向能力探测(显式检查
- estimated_peak_device_bytes 缺正值下界与类型校验,异常上报会使显存闸门失效 @
rtp_llm/model_loader/loader.py:359- 建议:对上报值加类型与正值校验并取下界,例如
max(legacy_budget, int(estimate)),非正值或非整数时 warn 并退回 legacy 估计。若确实有意完全信任 wheel 上报,请在实现处注释说明门槛放宽是有意的,并在文档中提示新旧路径显存判定口径的差异,便于运维在升级后关注显存水位。
- 建议:对上报值加类型与正值校验并取下界,例如
- load_config() 早于 NOGDS 环境归一化被调用,预检预算与实际配置不一致 @
rtp_llm/model_loader/loader.py:356- 建议:把 NOGDS →
FASTSAFETENSORS_CONFIG_JSON的归一化抽成一个共用函数(例如_normalize_fastsafetensors_config_env()),并在显存预检之前调用一次,使_fastsafetensors_transient_budget_bytes与AutoLoader构造读取同一份配置;或在预算函数内先完成同样的归一化再load_config(),避免两处配置来源分叉。
- 建议:把 NOGDS →
- full-stacked 模式的额外显存峰值未纳入准入预算 @
rtp_llm/utils/database.py:387- 建议:把
stacked_moe_mode与stacked_key_config传入_is_memory_enough_for_fastsafetensor/ 预算函数,在full-stacked下按expert_num与 stacked 张量规模额外计入整块张量的开销;若不便传参,至少在选择full-stacked时输出一条 warning,说明该模式下显存预检不覆盖整块 stacked 张量峰值,需要人工确认剩余显存。
- 建议:把
- 模式环境变量为空串时抛异常导致加载硬失败 @
rtp_llm/model_loader/loader.py:410- 建议:把空串视为未设置并回落默认值,例如
(os.environ.get(...) or "per-expert").strip() or "per-expert";并在database.py:319的二次校验处保持一致语义。若刻意要对空串 fail-fast,请在文档中明确写出「置空不等于取默认值」,避免运维按常规习惯清空变量后启动失败。
- 建议:把空串视为未设置并回落默认值,例如
- 新开关绕过 LoadConfig 配置面,取值集合多处重复且不入启动配置快照 @
rtp_llm/model_loader/loader.py:413- 建议:按仓库既有约定在
load_group_args.py注册--fastsafetensors_stacked_moe_mode(default="per-expert",取值用choices或str, Enum约束)并 bind 到LoadConfig;loader.py与database.py改为接收已校验的枚举,删除database.py的重复字面量分支,把取值集合以LoadMethod为先例收敛为单一来源。文档同步在 Load Config 表格补一行。若确定仅作临时性能对比开关,请在文档中标注为实验开关及预计移除时间。
- 建议:按仓库既有约定在
- 未安装 fastsafetensors 的平台每次加载都打印误导性 WARNING @
rtp_llm/model_loader/loader.py:362- 建议:把
has_module("fastsafetensors")前移到and链中_is_memory_enough_for_fastsafetensor()之前(顺带省掉一次无意义的显存查询),或在预算函数内先判断模块可用性、不可用时改用logging.debug;同时为estimate is None补一条 info 说明已退回 legacy 估计,使两条降级路径的日志级别与信息量一致。
- 建议:把
- 文档把生效前提写成显式 LOAD_METHOD,遗漏默认 auto 同样走 AutoLoader @
docs/backend/server_arguments.md:208- 建议:将生效条件改为「
LOAD_METHOD=fastsafetensors,或默认auto自动选中 fastsafetensors 时」,并补一句auto的选中条件与回滚方式(显式LOAD_METHOD=scratch),使运维能判断自己是否受影响、以及出问题时如何快速回退到 SCRATCH 路径。
- 建议:将生效条件改为「
- NOGDS 兼容开关的优先级结论未验证,且覆写不可逆污染进程级 env @
docs/backend/server_arguments.md:223- 建议:在应用覆盖时同时
os.environ.pop("FASTSAFETENSORS_CONFIG", None),让优先级承诺由 RTP 自身保证;或改为try/finally还原原值,把覆盖限制在单次加载范围内。文档相应改为「优先级由 fastsafetensors 决定,请只保留一种配置方式」,并注明该覆盖是进程级、日志中会出现对应 warning。
- 建议:在应用覆盖时同时
- 文档把 full-stacked 写成仅供性能对比,实为 wheel 能力不足时的回滚手段 @
docs/backend/server_arguments.md:229- 建议:补充 full-stacked 的两种用途(性能对比 + wheel 能力缺失时的临时兼容回退),注明其显存代价量级与「仅作临时措施、应尽快升级 wheel」的建议,使文档与错误信息的处置指引一致;补一句非法取值会 ValueError 快速失败,避免运维把拼写错误误判为静默降级;并在小节开头列出两种硬失败信号及对应处置方式。此外建议把
FASTSAFETENSORS_CONFIG、FASTSAFETENSORS_CONFIG_JSON、RTP_FASTSAFETENSORS_STACKED_MOE_MODE补进本页第 199-204 行的 Load Config 表格(无对应 CLI 参数时标注 env-only),与本页arg (ENV)登记约定保持一致。
- 建议:补充 full-stacked 的两种用途(性能对比 + wheel 能力缺失时的临时兼容回退),注明其显存代价量级与「仅作临时措施、应尽快升级 wheel」的建议,使文档与错误信息的处置指引一致;补一句非法取值会 ValueError 快速失败,避免运维把拼写错误误判为静默降级;并在小节开头列出两种硬失败信号及对应处置方式。此外建议把
- 文档 continue to use 措辞掩盖了默认调参变更且无回滚配置示例 @
docs/backend/server_arguments.md:237- 建议:改写该句,明确「bucket 大小、shm、copier 等参数不再由 RTP 传入,改由 fastsafetensors 默认值或 config JSON 决定」,并给出含 bucket/shm 字段的示例 JSON(键名与 wheel 侧一致),说明希望保持原有加载性能的部署应如何显式配置;同时在 PR description 补一组升级前后的加载耗时 / 峰值显存对比(至少一个大 MoE 模型、单机多卡),以支撑「默认值足够好」的判断。
- per-expert 拆分用例由 fake 自行完成拆分,用例名与实际覆盖不符 @
rtp_llm/utils/test/ckpt_database_test.py:119- 建议:把用例改名为反映真实断言的内容(如
test_split_templates_are_forwarded_and_loader_is_closed),并补一条让 fake 产出真实torch.Tensor且 key 与stacked_key_config对齐的用例,真正走通 per-expert 拆分分支。另补一条针对真实 wheel 的契约用例:用unittest.skipUnless在装有 fastsafetensors 的目标上以inspect.signature(AutoLoader.__init__)断言两个关键 kwarg 存在,或在 smoke 层加一条LOAD_METHOD=fastsafetensors的 stacked MoE 用例,补回移除版本闸门后失去的漂移防护。
- 建议:把用例改名为反映真实断言的内容(如
- 缺少对 clone 语义的断言,去掉 clone 后测试仍会通过 @
rtp_llm/utils/test/ckpt_database_test.py:273- 建议:让 fake 持有源张量引用,在
list(...)消费结束后执行src.fill_(0),再断言已产出的两片切片数值未变;或直接断言result[0][1].data_ptr() != src[0].data_ptr()。同时补一条num_experts=1的边界用例,确认单专家 stacked 张量仍产出恰好一个 key。
- 建议:让 fake 持有源张量引用,在
- NOGDS 兼容开关只覆盖覆写方向,缺少反向断言与 database 侧非法模式断言 @
rtp_llm/utils/test/ckpt_database_test.py:321- 建议:补一条 NOGDS 未设置时的用例,断言
AutoLoader观察到的配置与用户原设值一致;把现有用例的断言从「调用后环境变量是否残留」改为只校验传入时刻的有效配置(observed_config),避免把进程级副作用写死为期望行为;再补一条同时设置FASTSAFETENSORS_CONFIG与 inline JSON 的用例把优先级固化为可回归行为,以及一条直接调用fastsafetensors_weights_iterator(..., stacked_moe_mode="surprise")断言 ValueError 的用例。
- 建议:补一条 NOGDS 未设置时的用例,断言
- 重复的 fake 脚手架与两种模块打桩写法并存 @
rtp_llm/utils/test/ckpt_database_test.py:90- 建议:抽一个参数化的 fake 工厂(例如
_install_fake_fastsafetensors(*, split_templates_sink=None, filter_sink=None, weights=(), support_split=True))供各用例复用,把构造签名收敛到单处;模块打桩统一改用patch.dict(sys.modules, {...}),与test_stacked_moe_weight.py保持同一惯例,同时省掉手写的sys.modules保存/还原逻辑。
- 建议:抽一个参数化的 fake 工厂(例如
- copyout filter 用例未隔离模式环境变量,文档给出的场景会直接使其失败 @
rtp_llm/model_loader/test/test_stacked_moe_weight.py:315- 建议:给该用例(或类级
setUp/tearDown)加显式隔离:patch.dict("os.environ", ...)配合os.environ.pop("RTP_FASTSAFETENSORS_STACKED_MOE_MODE", None),做法可参考同 PRFastsafetensorsAutoLoaderTest.setUp/tearDown对FASTSAFETENSORS_*的处理以保持同仓一致;并补一个只针对_fastsafetensors_stacked_moe_mode()的用例,在变量确定不存在与为空串两种情况下断言期望返回值。
- 建议:给该用例(或类级
- 旧 wheel 的异常回退分支与 0 值边界零覆盖,与 docstring 承诺不符 @
rtp_llm/model_loader/test/test_stacked_moe_weight.py:401- 建议:补三条用例:构造无
load_config属性的types.ModuleType("fastsafetensors")覆盖 ImportError/AttributeError 分支;让load_config抛ValueError覆盖异常分支,两者均断言返回3 * max_file_size并用assertLogs校验降级日志已输出;再加estimated_peak_device_bytes=0与负值的边界断言,明确期望是「按配置放行」还是「视作未配置并回退」,并与实现侧的下界校验保持一致。
- 建议:补三条用例:构造无
P3
- use_tqdm_on_load 成为死参数,加载进度可观测性静默丢失 @
rtp_llm/utils/database.py:312- 建议:二选一:若上游 config 已提供等价的进度开关,把该参数映射到对应配置项并在文档中说明;若确定不再支持,删除
use_tqdm_on_load形参与内部透传、同步修改loader.py:487与三处测试调用,避免留下让调用方误判的死参数。同时确认loader.py:520-527的周期性进度日志是否足以替代 tqdm,否则建议把采样间隔调密或改为按耗时打点。
- 建议:二选一:若上游 config 已提供等价的进度开关,把该参数映射到对应配置项并在文档中说明;若确定不再支持,删除
- 预算 helper 的 docstring 字段名与实现不符,且异常捕获过宽、错误语义不一致 @
rtp_llm/model_loader/loader.py:352- 建议:把 docstring 中的
max_batch_bytes改为estimated_peak_device_bytes;删去冗余的ModuleNotFoundError与不可达的AttributeError;estimate is None时补一条 info 说明已退回 legacy 估计;并统一错误语义:定位为 best-effort 读取则把OSError一并纳入(收敛为(ImportError, ValueError, OSError)),定位为 fail-fast 则移除ValueError的静默兜底,避免「看起来防御但真正的失败模式没兜住」。
- 建议:把 docstring 中的
- 新增纯逻辑用例被 H20 GPU 目标门禁,归属类职责不符且文件头 Covers 未更新 @
rtp_llm/model_loader/test/test_stacked_moe_weight.py:302- 建议:把 copyout 与 stacked-moe-mode 相关用例拆到独立类(如
TestFastsafetensorsCopyoutFilter、TestStackedMoeModeResolution),与已独立的TestFastsafetensorsTransientBudget风格一致;同步在文件头Covers:补上「rank-local copyout filter」「stacked MoE 模式解析」「fastsafetensors 瞬时内存预算」三项。若这批纯逻辑断言不需要真实 GPU,建议另建一个不带 H20 tag 的 py_test 目标承载,减少稀缺 GPU 占用并让它们在更多 CI 目标上执行。
- 建议:把 copyout 与 stacked-moe-mode 相关用例拆到独立类(如
- UE8M0 broadcast shim 的适用性断言随 AutoLoader 迁移失去可验证依据 @
rtp_llm/utils/torch_patch.py:37- 建议:把注释中的断言改为可验证形式:明确写出「已验证的 wheel 版本 + loader 后端组合」,并说明切换后端需重新确认;更稳妥的做法是把 shim 扩展到
scatter/all_gather_into_tensor等同类入口(同样只在float8_e8m0fnu上触发,代价极低),或在多 rank UE8M0 加载路径上补一条 smoke 覆盖,使后端切换导致的失效能被自动发现而非上线时暴露。
- 建议:把注释中的断言改为可验证形式:明确写出「已验证的 wheel 版本 + loader 后端组合」,并说明切换后端需重新确认;更稳妥的做法是把 shim 扩展到
Checklist Findings (18 fail / 48 total)
General Principles Checklist
- [6.1] Architecture — 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全 → issue
UE8M0 broadcast shim 的适用性断言随 AutoLoader 迁移失去可验证依据
该 shim 只 patchdist.broadcast(torch_patch.py:68-77),用于绕开 NCCL 拒绝Float8_e8m0fnu的限制;原注释的依据是 RTP 自己实现/子类化了ParallelLoader,可确认「每条 UE8M0 传输都走pg.broadcast」。本 PR 把注释改为「AutoLoaderroutes UE8M0 transfers throughpg.broadcast」,但AutoLoader是配置驱动的调度器,后端还可由最终用户通过FASTSAFETENSORS_CONFIG_JSON(文档第 214 行示例"loader":"base")自由切换,不同 copier 是否只用 broadcast、是否改用scatter/all_gather已不在本仓可验证范围内。若某后端使用 broadcast 之外的集合通信,DSv4-Flash 等 UE8M0 权重 scale 在多 rank 加载时会以 NCCL dtype 错误崩溃。 - [6.1] Architecture — 分层边界:新概念在正确层级,不泄漏内部 → issue
新开关绕过 LoadConfig 配置面,取值集合多处重复且不入启动配置快照
同一 Load Config 章节其余四个开关(--load_method/--force_cpu_load_weights/--loader_recycle_handles/--moe_pure_tp_preshard)都在load_group_args.py:9-40通过add_argument(env_name=..., bind_to=...)注册,可 CLI 设置、可进配置快照、启动期完成校验。新开关只在loader.py:410用裸os.environ.get读取:既不进--help与参数表,也无法 CLI 覆盖,非法值要等后端进程真正加载权重时才抛 ValueError。取值集合{"per-expert","full-stacked"}在loader.py:413(带.strip())、database.py:319(不 strip)、database.py:315默认值字面量、文档正文各写一份,新增第三种模式需同步四处。仓内已有 `LoadMethod(str, enum. - [6.1] Architecture — 可观测性:日志/指标/超时可操作、非噪声 → issue
预算 helper 的 docstring 字段名与实现不符,且异常捕获过宽、错误语义不一致
docstring 第 352 行称「当max_batch_bytes未设置时保留历史估算」,代码实际读取的是estimated_peak_device_bytes(:359),字段名对不上,易误导维护者去找不存在的配置项。getattr字面量探测在属性改名或 wheel 不支持时静默回落 legacy,且estimate is None这条路径没有任何日志。捕获列表中ModuleNotFoundError是ImportError子类(冗余),AttributeError在getattr(..., None)下不可达;而FASTSAFETENSORS_CONFIG指向不存在文件时load_config()抛出的OSError不在捕获范围内会直接冒泡——同一函数对配置读取失败呈现「静默降级」与「直接崩溃」两种不一致语义。 - [6.1] Architecture — 回滚路径:风险行为存在运维回滚手段 → issue
文档 continue to use 措辞掩盖了默认调参变更且无回滚配置示例
文档称「ordinary tensors continue to use the FastSafeTensors bucket and rank-local-copy settings」,读起来像行为不变。但 diff 显示改造前 RTP 侧显式传入bbuf_size_kb=1024*1024*2(2GB 量级批缓冲)、use_shm=not use_nogds、nogds=use_nogds、use_tqdm_on_load,改造后database.py:352-363只传pg、文件列表、device与两个新 kwarg——bucket 大小、shm、copier 取值全部改由库默认或FASTSAFETENSORS_CONFIG_JSON决定。这是对所有既有 safetensors 部署(默认auto亦命中)的隐式调参变更:若库默认批缓冲显著小于 2GB,权重加载耗时可能回退,而文档既未标注为行为变更,也未给出恢复原取值的迁移映射。 - [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue
NOGDS 兼容开关的优先级结论未验证,且覆写不可逆污染进程级 env
文档据「inline JSON 优先于文件路径」推出 NOGDS「therefore takes priority over other fastsafetensors configuration」,但该优先级完全由 wheel 内部决定,仓内既无实现保证也无验证手段;database.py:344-351对同时设置的FASTSAFETENSORS_CONFIG文件路径不做任何处理——若 wheel 实际以文件配置优先,兼容开关会在它本要修复的 dev 场景中静默失效。同时该覆写就地写入os.environ且不还原,用户原设值在本进程后续所有加载(含 ViT/EPLB 二次加载、子进程继承)中被永久替换,文档未说明该副作用不可逆。 - [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue
UE8M0 broadcast shim 的适用性断言随 AutoLoader 迁移失去可验证依据
该 shim 只 patchdist.broadcast(torch_patch.py:68-77),用于绕开 NCCL 拒绝Float8_e8m0fnu的限制;原注释的依据是 RTP 自己实现/子类化了ParallelLoader,可确认「每条 UE8M0 传输都走pg.broadcast」。本 PR 把注释改为「AutoLoaderroutes UE8M0 transfers throughpg.broadcast」,但AutoLoader是配置驱动的调度器,后端还可由最终用户通过FASTSAFETENSORS_CONFIG_JSON(文档第 214 行示例"loader":"base")自由切换,不同 copier 是否只用 broadcast、是否改用scatter/all_gather已不在本仓可验证范围内。若某后端使用 broadcast 之外的集合通信,DSv4-Flash 等 UE8M0 权重 scale 在多 rank 加载时会以 NCCL dtype 错误崩溃。 - [6.1] Quality — 逻辑变更未混入无关格式化 → issue
新增纯逻辑用例被 H20 GPU 目标门禁,归属类职责不符且文件头 Covers 未更新
TestBuildStackedKeyConfig的 docstring 声明只测_build_stacked_key_config,但类内新增的:302、:315、:361、:373四个用例分别测试 copyout key 构造、_load_from_fastsafetensor接线与环境变量解析,与该类主题无关;文件头Covers:(:3-7)三条清单也未增补新覆盖面。此外这批用例(含TestFastsafetensorsTransientBudget)全是纯逻辑断言,却被test/BUILD:5-13的tags=["H20"]+exec_properties={'gpu':'H20'}门禁,占用稀缺 GPU 执行槽且在无 H20 的环境无法本地直跑。 - [6.1] Software Engineering — DRY:重复非平凡逻辑被抽取或显式复用 → issue
重复的 fake 脚手架与两种模块打桩写法并存
FastsafetensorsAutoLoaderTest的 6 个用例各自重复定义几乎相同的FakeSingleGroup与FakeAutoLoader(:123-149、:184-208、:228-252、:277-283、:294-305、:324-350),仅观察点不同;构造签名(pg, files, device, local_copyout_filter, dim0_split_templates)被手抄五遍,一旦真实 wheel 签名变化需同步修改五处。同时本 PR 引入了两种模块打桩写法:本文件用sys.modules["fastsafetensors"] = fake_module配合手写 setUp/tearDown 还原,test_stacked_moe_weight.py:395用patch.dict(sys.modules, ...),同一功能两套惯例增加后续维护者的认知负担。 - [6.1] Software Engineering — KISS/YAGNI:无投机性抽象 → issue
use_tqdm_on_load 成为死参数,加载进度可观测性静默丢失
改造前use_tqdm_on_load被透传为ParallelLoader(use_tqdm_on_load=...),驱动加载进度显示;改造后loader_kwargs不再包含它,内部def iterator(device, use_tqdm_on_load)(database.py:325)与return iterator(device, use_tqdm_on_load)(:395)仍保留形参,函数体内没有任何使用点。唯一生产调用方loader.py:487仍硬编码传True,即调用方以为开启了进度显示、实际已完全失效。大模型权重加载耗时数分钟,进度输出丢失会影响现场排障(难以区分「加载慢」与「加载卡死」)。 - [6.1] Software Engineering — SRP:模块/类职责单一 → issue
新增纯逻辑用例被 H20 GPU 目标门禁,归属类职责不符且文件头 Covers 未更新
TestBuildStackedKeyConfig的 docstring 声明只测_build_stacked_key_config,但类内新增的:302、:315、:361、:373四个用例分别测试 copyout key 构造、_load_from_fastsafetensor接线与环境变量解析,与该类主题无关;文件头Covers:(:3-7)三条清单也未增补新覆盖面。此外这批用例(含TestFastsafetensorsTransientBudget)全是纯逻辑断言,却被test/BUILD:5-13的tags=["H20"]+exec_properties={'gpu':'H20'}门禁,占用稀缺 GPU 执行槽且在无 H20 的环境无法本地直跑。 - [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue
copyout 谓词跨 rank 不对称,EP>1 语义未固化且零覆盖
loader.py:490把required_checkpoint_keys.__contains__传给参与 rank 间 broadcast 的AutoLoader。该集合由 rank 无关的stacked_key_config与tensor_to_weight_map(loader.py:616→get_tensor_names)合并;后者对 MoE 权重经ffn_weight.py:706-720调用load_config.get_selected_experts,而load_config.py:93-97按expert_per_ep * ep_rank切片,故ep_size>1时该谓词在各 rank 返回不同结果。参数名local_copyout_filter暗示 rank 局部语义(故不判定为必然挂死),但该不变量既未在代码中声明也无校验:若过滤同时影响集合通信参与度会在加载期挂死,即使仅影响本地物化,误过滤也会静默走loader.py:547的 database 慢速回退( - [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue
旧 wheel 的异常回退分支与 0 值边界零覆盖,与 docstring 承诺不符
TestFastsafetensorsTransientBudget只覆盖estimated_peak_device_bytes为具体值(:388)与为None(:401)两种情况。而_fastsafetensors_transient_budget_bytes的 docstring 声称主要兼容目标是「loading an older wheel」,对应loader.py:361-366的except (ImportError, ModuleNotFoundError, AttributeError, ValueError)降级分支——零覆盖;结合 cuda12_9 当前仍锁0.1.19rc5+ali,该分支恰是最可能真实生效的路径,也是未安装 fastsafetensors 时预检能否正确工作的唯一保障。此外estimate=0属「非 None」,会使返回 0,把准入判定放宽为(free_mem - model_mem) > 0(loader.py:338),该边界同样无用例守护。 - [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue
旧 wheel 的异常回退分支与 0 值边界零覆盖,与 docstring 承诺不符
TestFastsafetensorsTransientBudget只覆盖estimated_peak_device_bytes为具体值(:388)与为None(:401)两种情况。而_fastsafetensors_transient_budget_bytes的 docstring 声称主要兼容目标是「loading an older wheel」,对应loader.py:361-366的except (ImportError, ModuleNotFoundError, AttributeError, ValueError)降级分支——零覆盖;结合 cuda12_9 当前仍锁0.1.19rc5+ali,该分支恰是最可能真实生效的路径,也是未安装 fastsafetensors 时预检能否正确工作的唯一保障。此外estimate=0属「非 None」,会使返回 0,把准入判定放宽为(free_mem - model_mem) > 0(loader.py:338),该边界同样无用例守护。
RTP-LLM Checklist
- [I] 代码质量 — 删除或重命名内部 file、registry entry、model name、metric enum、op binding、plugin symbol 时,必须全仓搜索消费者,并提供替代实现、迁移说明或 smoke 覆盖;只有暴露到 HTTP/RPC/config/persisted format 时才按外部兼容性处理 → issue
硬依赖 fastsafetensors 新版 AutoLoader 能力但未对齐 wheel pin,删除版本闸门后 auto 路径无探测也无回退
database.py:317无条件from fastsafetensors import AutoLoader,:352-354无条件传local_copyout_filter,loader.py:356-359读load_config().estimated_peak_device_bytes。改动前仅 stacked MoE 才触达带闸门(_REQUIRED_FST_VERSION="0.1.19"+startswith)的子类,现在所有 fastsafetensors 加载都经AutoLoader,闸门随文件删除且无替代。本 PR 7 个 diff 文件不含任何deps/:cuda12_9 仍锁0.1.19rc5+ali,cuda13/arm 为0.1.20+ali,两者本就不一致。loader.py:262-269的默认 AUTO 仅用has_module()(module_util.py:13只查 find_spec),选中后 ImportError 无 SCRATCH 回退。`dat - [I] 代码质量 — 同一功能用统一工具函数 → issue
重复的 fake 脚手架与两种模块打桩写法并存
FastsafetensorsAutoLoaderTest的 6 个用例各自重复定义几乎相同的FakeSingleGroup与FakeAutoLoader(:123-149、:184-208、:228-252、:277-283、:294-305、:324-350),仅观察点不同;构造签名(pg, files, device, local_copyout_filter, dim0_split_templates)被手抄五遍,一旦真实 wheel 签名变化需同步修改五处。同时本 PR 引入了两种模块打桩写法:本文件用sys.modules["fastsafetensors"] = fake_module配合手写 setUp/tearDown 还原,test_stacked_moe_weight.py:395用patch.dict(sys.modules, ...),同一功能两套惯例增加后续维护者的认知负担。
Python Static-First Checklist
- [P.A] 静态结构与类型纪律 — 字符串分发用 Enum/Literal → issue
新开关绕过 LoadConfig 配置面,取值集合多处重复且不入启动配置快照
同一 Load Config 章节其余四个开关(--load_method/--force_cpu_load_weights/--loader_recycle_handles/--moe_pure_tp_preshard)都在load_group_args.py:9-40通过add_argument(env_name=..., bind_to=...)注册,可 CLI 设置、可进配置快照、启动期完成校验。新开关只在loader.py:410用裸os.environ.get读取:既不进--help与参数表,也无法 CLI 覆盖,非法值要等后端进程真正加载权重时才抛 ValueError。取值集合{"per-expert","full-stacked"}在loader.py:413(带.strip())、database.py:319(不 strip)、database.py:315默认值字面量、文档正文各写一份,新增第三种模式需同步四处。仓内已有 `LoadMethod(str, enum. - [P.A] 静态结构与类型纪律 — 禁止 getattr/setattr literal 访问 → issue
预算 helper 的 docstring 字段名与实现不符,且异常捕获过宽、错误语义不一致
docstring 第 352 行称「当max_batch_bytes未设置时保留历史估算」,代码实际读取的是estimated_peak_device_bytes(:359),字段名对不上,易误导维护者去找不存在的配置项。getattr字面量探测在属性改名或 wheel 不支持时静默回落 legacy,且estimate is None这条路径没有任何日志。捕获列表中ModuleNotFoundError是ImportError子类(冗余),AttributeError在getattr(..., None)下不可达;而FASTSAFETENSORS_CONFIG指向不存在文件时load_config()抛出的OSError不在捕获范围内会直接冒泡——同一函数对配置读取失败呈现「静默降级」与「直接崩溃」两种不一致语义。 - [P.G] 测试规范 — mock/fake/stub 不得替代本次声称覆盖的生产边界 → issue
copyout filter 用例未隔离模式环境变量,文档给出的场景会直接使其失败
test_rank_local_copyout_filter_is_always_applied以assertEqual(observed_modes, ["per-expert"])(:358)断言默认模式,但整个用例未 patch 也未清理RTP_FASTSAFETENSORS_STACKED_MOE_MODE,_fastsafetensors_stacked_moe_mode()直接读宿主os.environ(loader.py:410),所在类TestBuildStackedKeyConfig无setUp/tearDown。本 PR 文档第 233 行正主动指导运维export RTP_FASTSAFETENSORS_STACKED_MOE_MODE=full-stacked:该变量一旦留在开发机 shell 或 CI 容器中,用例即因环境而非代码失败;拼写错误还会抛出与用例意图无关的 ValueError。相邻的:361、:373两个用例都已使用patch.dict,隔离方式不一致。
Strengths
- 删除
PerExpertParallelLoader移除了对fb._get_rank_lidx、fb.rank_loaders、fb.instantiated、fb.auto_mem_delete、factory.metadata.tensors、factory.free_dev_ptrs的侵入式依赖,以及一份从上游_consume_single_batch复制来的分支逻辑,把版本敏感实现责任交回 wheel,跨版本脆弱性显著下降。 - 删除动作干净完整:全仓搜索
PerExpertParallelLoader/per_expert_parallel_loader/_REQUIRED_FST_VERSION零命中,BUILD用glob()无需改动,torch_patch.py:29-39中引用该类的 UE8M0 注释同步改写,未留悬空引用或过期注释,符合 R.I.2 的消费者搜索要求。 _build_fastsafetensors_local_copyout_keys(loader.py:387-398)同时纳入 stacked raw key 与展开后的 per-expert key,使 copyout 过滤在「按原始键过滤」与「按拆分后键过滤」两种上游语义下都成立,避开了误杀 stacked MoE 的典型陷阱,docstring 解释的是 WHY 而非 WHAT。- 默认行为不变且回滚清晰:
per-expert(有界显存)为默认、full-stacked为显式 opt-in;能力缺失被固化为 fail-fast 而非静默退回旧路径(缺AutoLoader→ ImportError、缺dim0_split_templates→ 带处置指引的 RuntimeError、非法 mode → ValueError),三者均以带match的assertRaisesRegex校验。 - 测试不依赖真实 wheel:通过
types.ModuleType注入 fake 模块,FastsafetensorsAutoLoaderTest.setUp/tearDown(ckpt_database_test.py:91-117)完整保存并还原三个FASTSAFETENSORS_*变量与sys.modules条目,是标准 hermetic 范式,不污染同进程其他用例。 test_full_stacked_mode_disables_prebroadcast_split用真实torch.Tensor驱动生产侧 dim0 切分并逐片比值,是本次唯一真正执行 RTP 侧拆分语义的用例。- 本次 diff 已把上一轮混入的 CUDA13 flashinfer 打包 /
arch_select.bzl/ 依赖 pin 改动全部移出,PR 收敛为单一主题的 7 个文件,commit 原子性与可 bisect 性明显改善。
| len(stacked_key_config), | ||
| ) | ||
|
|
||
| required_checkpoint_keys = self._build_fastsafetensors_local_copyout_keys( |
There was a problem hiding this comment.
[P2] copyout 谓词跨 rank 不对称,EP>1 语义未固化且零覆盖
loader.py:490 把 required_checkpoint_keys.__contains__ 传给参与 rank 间 broadcast 的 AutoLoader。该集合由 rank 无关的 stacked_key_config 与 tensor_to_weight_map(loader.py:616 → get_tensor_names)合并;后者对 MoE 权重经 ffn_weight.py:706-720 调用 load_config.get_selected_experts,而 load_config.py:93-97 按 expert_per_ep * ep_rank 切片,故 ep_size>1 时该谓词在各 rank 返回不同结果。参数名 local_copyout_filter 暗示 rank 局部语义(故不判定为必然挂死),但该不变量既未在代码中声明也无校验:若过滤同时影响集合通信参与度会在加载期挂死,即使仅影响本地物化,误过滤也会静默走 loader.py:547 的 database 慢速...
建议: 在 _build_fastsafetensors_local_copyout_keys 的 docstring 中显式声明该谓词允许跨 rank 分叉并给出上游语义依据;或在传入前跨 process group 校验键集合一致性(all_gather hash,不一致即 fail-fast 而非挂死)。补一个 ep_size=2 的用例(gloo + CPU 假 checkpoint 即可),断言两个 ep_rank 各自的 required_checkpoint_keys 恰好覆盖本 rank 所需 key 且 stacked raw key 在两 rank 上一致。另建议把 loader.py:556 的 fallback 日志从 info 升为 warning 或补一条 metric,使过滤误杀导致的静默回退可观测。
Checklist: [6.1] 分布式/跨平台变更有对应覆盖
| from fastsafetensors import load_config | ||
|
|
||
| config = load_config() | ||
| estimate = getattr(config, "estimated_peak_device_bytes", None) |
There was a problem hiding this comment.
[P2] estimated_peak_device_bytes 缺正值下界与类型校验,异常上报会使显存闸门失效
loader.py:335-344 把原先与 checkpoint 相关的 3 * max_file_size 门槛整体替换为 _fastsafetensors_transient_budget_bytes(),后者只读 load_config().estimated_peak_device_bytes(:358-360)——一个与实际 shard / 单张量大小无关的纯 loader 配置量。return legacy_budget if estimate is None else estimate 仅对 None 兜底:上报 0 或极小值时 transient_mem 被抹平,门槛退化为 free_mem > model_mem,max_file_size 只剩打日志用途(:334)。预检假通过后 AUTO 已选定 fastsafetensors,不会再退回 SCRATCH,失败形式是加载期 OOM。
建议: 对上报值加类型与正值校验并取下界,例如 max(legacy_budget, int(estimate)),非正值或非整数时 warn 并退回 legacy 估计。若确实有意完全信任 wheel 上报,请在实现处注释说明门槛放宽是有意的,并在文档中提示新旧路径显存判定口径的差异,便于运维在升级后关注显存水位。
| """ | ||
| legacy_budget = 3 * max_file_size | ||
| try: | ||
| from fastsafetensors import load_config |
There was a problem hiding this comment.
[P2] load_config() 早于 NOGDS 环境归一化被调用,预检预算与实际配置不一致
_fastsafetensors_transient_budget_bytes 在 _is_memory_enough_for_fastsafetensor(loader.py:335)阶段调用 load_config() 读取预算,而把 FASTSAFETENSORS_NOGDS=1 归一化成 FASTSAFETENSORS_CONFIG_JSON 的逻辑位于其后的 database.py:344-351,只在真正构造 AutoLoader 之前执行。因此在 NOGDS 场景下,门槛判断依据的 config(用户原始配置或库默认)与实际生效的 base/nogds config 可能是两份不同配置,二者预算差异会直接导致准入判断偏乐观或偏保守。
建议: 把 NOGDS → FASTSAFETENSORS_CONFIG_JSON 的归一化抽成一个共用函数(例如 _normalize_fastsafetensors_config_env()),并在显存预检之前调用一次,使 _fastsafetensors_transient_budget_bytes 与 AutoLoader 构造读取同一份配置;或在预算函数内先完成同样的归一化再 load_config(),避免两处配置来源分叉。
| # RTP-LLM's ``PerExpertParallelLoader`` route every UE8M0 transfer | ||
| # through ``pg.broadcast`` (dim=-1 in fastsafetensors / per-expert | ||
| # broadcast in PerExpertParallelLoader); only the broadcast wrapper is | ||
| # not exercised by fastsafetensors -- ``AutoLoader`` routes UE8M0 |
There was a problem hiding this comment.
[P3] UE8M0 broadcast shim 的适用性断言随 AutoLoader 迁移失去可验证依据
该 shim 只 patch dist.broadcast(torch_patch.py:68-77),用于绕开 NCCL 拒绝 Float8_e8m0fnu 的限制;原注释的依据是 RTP 自己实现/子类化了 ParallelLoader,可确认「每条 UE8M0 传输都走 pg.broadcast」。本 PR 把注释改为「AutoLoader routes UE8M0 transfers through pg.broadcast」,但 AutoLoader 是配置驱动的调度器,后端还可由最终用户通过 FASTSAFETENSORS_CONFIG_JSON(文档第 214 行示例 "loader":"base")自由切换,不同 copier 是否只用 broadcast、是否改用 scatter/all_gather 已不在本仓可验证范围内。若某后端使用 broadcast 之外的集合通信,DSv4-Flash 等 UE8M0 权重 scale 在多 rank 加载时会以 NCCL dtype 错误崩溃。
建议: 把注释中的断言改为可验证形式:明确写出「已验证的 wheel 版本 + loader 后端组合」,并说明切换后端需重新确认;更稳妥的做法是把 shim 扩展到 scatter / all_gather_into_tensor 等同类入口(同样只在 float8_e8m0fnu 上触发,代价极低),或在多 rank UE8M0 加载路径上补一条 smoke 覆盖,使后端切换导致的失效能被自动发现而非上线时暴露。
Checklist: [6.1] 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全;[6.1] 错误语义:fail-fast/retry/fallback/silent 行为显式
Keep planning and broadcast collective participation global while limiting post-broadcast copyout to tensors required by each rank. Bound stacked MoE broadcast memory and cover the rank-local and batch-cap behavior in loader tests.
61bc76b to
9c76e93
Compare
LLLLKKKK
left a comment
There was a problem hiding this comment.
AI Code Review - PR #1354
Status: BLOCKING
Summary: P0/0 · P1/1 · P2/12 · P3/5
Reviewed: commit 9c76e93ad022 · 2026-08-31 16:47 UTC+8
Blocking Issues
P1
- 新增 py_test 缺少 unittest.main(),15 条策略用例全部空跑并报 PASS @
rtp_llm/model_loader/test/test_fastsafetensors_loader_policy.py:299- 建议:在文件末尾补
if __name__ == "__main__":+unittest.main(),与同目录既有约定一致;补齐后实跑该 target 并确认输出为Ran 15 tests而非Ran 0 tests(静态走查未发现用例自身有断言错误)。建议同时全仓排查是否还有其他 py_test 源文件存在同类缺失;若希望长期免疫,可在 BUILD 层统一改用带 runner 的宏而非裸py_test。
- 建议:在文件末尾补
Non-blocking Suggestions
P2
- 默认 cuda12_9 wheel pin 未随新能力契约对齐,静默降级在 CI 与线上均不可观测 @
docs/backend/server_arguments.md:206- 建议:建议三项一起落地:一是文档补「能力 → 最低 fastsafetensors 版本 → 实际生效路径」矩阵(示例中的
parallel.use_tqdm_on_load也应标注起始版本),并同步 bumpdeps/requirements_torch_gpu_cuda12_9.txt及其 lock,使默认平台确实具备 per-expert 能力;二是让安装态契约测试在「应当支持」的镜像中 fail 而非 skip(例如以环境变量声明期望能力档位,未达到即断言失败),使降级在 CI 立即暴露;三是在加载入口固定打印一条 INFO 输出最终生效的 mode 与降级原因,或上报可告警的降级指标,使线上可自查。
- 建议:建议三项一起落地:一是文档补「能力 → 最低 fastsafetensors 版本 → 实际生效路径」矩阵(示例中的
- 能力探测对 **kwargs 签名误判,使显存预算与实际交付方式不一致 @
rtp_llm/utils/database.py:81- 建议:让 per-expert 能力探测改用严格命名参数判定,与契约测试保持一致(不用 VAR_KEYWORD 兜底);若确需保留
**kwargs兜底以适配上游包装器,则在仅靠 VAR_KEYWORD 通过时把 effective mode 降级为full-stacked并打 warning,使显存预算与实际交付方式始终同口径。该判定应只有一处实现,供 loader、database 与契约测试共同调用,避免生产与测试各写一套。
- 建议:让 per-expert 能力探测改用严格命名参数判定,与契约测试保持一致(不用 VAR_KEYWORD 兜底);若确需保留
- 显存预算完全信任上游上报值且无下界,AUTO 准入可能由回退 scratch 变为加载期 OOM @
rtp_llm/model_loader/loader.py:433- 建议:建议保留下界或余量,例如
budget = max(int(estimate), legacy_budget)或与model_mem成比例的经验值,并在日志中同时打印estimate与最终采用的budget,便于线上区分「包上报值生效」与「走 legacy 估算」。测试侧二选一:补一条极小正值被夹到 legacy 下界的用例,或在用例名与注释中写明「无条件信任上报值」这一取舍及 OOM 风险,避免后续读者误判为疏漏。
- 建议:建议保留下界或余量,例如
- 显式 LOAD_METHOD=fastsafetensors 路径不做内存预检,降级为 full-stacked 时无预算兜底 @
rtp_llm/model_loader/loader.py:304- 建议:为显式
LOAD_METHOD=fastsafetensors且发生 per-expert → full-stacked 降级的场景也执行一次内存预检,或至少把 warning 升级为带max_file_size与可用显存数值的显式提示,并在文档中明确「旧 wheel 上请改用LOAD_METHOD=scratch」,使显式路径与 AUTO 路径的风险兜底口径一致。
- 建议:为显式
- 两处能力探测的异常集合不一致,AttributeError 会使「包过旧不致启动失败」的承诺失效 @
rtp_llm/model_loader/loader.py:454- 建议:统一两处的异常集合,使「包不兼容一律回退 scratch」在所有 import 期异常类型下成立;同时对「
OSError/RuntimeError环境故障」与「签名缺关键字的版本能力不足」两类原因在日志中区分措辞,避免运维把环境问题误判为 wheel 过旧。
- 建议:统一两处的异常集合,使「包不兼容一律回退 scratch」在所有 import 期异常类型下成立;同时对「
- NOGDS 兼容 env 覆写发生在首次 import fastsafetensors 之后,且不覆盖 FASTSAFETENSORS_CONFIG @
rtp_llm/model_loader/loader.py:292- 建议:把
_apply_fastsafetensors_env_compat()提到任何能力探测(即第一次import fastsafetensors)之前,或让_fastsafetensors_capability_error自身先应用 env 兼容再 import,文档表述同步收紧为「在读取上游配置与首次 import 之前」;在检测到FASTSAFETENSORS_CONFIG已设置时把 warning 措辞改为「可能不生效,最终优先级由安装包决定」,并在文档 230-236 段声明该限制。测试侧补一条不 mock_resolve_fastsafetensors_mode的用例:用 fake module 记录 import 时刻的FASTSAFETENSORS_CONFIG_JSON,断言其已是 nogds 配置。
- 建议:把
- 新增开关未注册进 server_args / LoadConfig,表格检索不到且不入启动配置日志 @
docs/backend/server_arguments.md:245- 建议:按既有约定注册
--fastsafetensors_stacked_moe_mode(env_name保持不变、取值受限、默认per-expert),登记进LoadConfig随启动 dump,并把该行补入 Load Config 表格,正文只保留背景说明。若坚持 env-only 过渡形态,请在小节显式写明「不提供命令行参数、不在--help展示、不进入启动配置 dump,per-expert 稳定后移除」,并至少在加载入口 INFO 日志固定打印一次最终生效的 mode 与降级原因。
- 建议:按既有约定注册
- 新开关的非法值 fail-fast 与「仅在 fastsafetensors 路径生效」两种语义均未文档化 @
docs/backend/server_arguments.md:248- 建议:在取值说明后补两句:一是「取值区分大小写且必须使用连字符,除
per-expert/full-stacked/ 空值外的任何取值都会在权重加载阶段抛ValueError导致启动失败,不会回落默认值」;二是「该开关仅在实际走 fastsafetensors 路径时生效,LOAD_METHOD=scratch或非 safetensors 权重下设置无效」。同时补充auto模式下显存预检不足同样会退回 scratch,避免运维把整节误读为「永不失败」。
- 建议:在取值说明后补两句:一是「取值区分大小写且必须使用连字符,除
- 唯一触达真实 wheel 的契约用例被四层 skip 短路,且 skip 文案与生产降级语义不符 @
rtp_llm/utils/test/ckpt_database_test.py:409- 建议:把 418 行拆开:缺
AutoLoader才用 scratch 措辞,缺load_config改为「仍走 fastsafetensors,使用 legacy 三分片估算」;422 行改为「仍走 fastsafetensors,全量物化 + 消费端过滤」,与 database.py / loader.py 及文档的三级降级语义一一对应。并参照同文件ckpt_database_test_rocm的env+args写法新增一个显式声明 fastsafetensors 依赖的严格目标,在该目标下缺能力即 assert 失败;至少把分类结果(scratch / full-stacked / per-expert)断言成显式枚举并打印到测试输出,使 CI 日志能看出镜像落在哪一档。
- 建议:把 418 行拆开:缺
- 决定加载方式的 _is_memory_enough_for_fastsafetensor 与 AUTO 成功主路径零覆盖 @
rtp_llm/model_loader/test/test_fastsafetensors_loader_policy.py:203- 建议:补四类用例:(1)
object.__new__(ModelLoader)+ 桩_weights_info/_load_config直测_is_memory_enough_for_fastsafetensor,断言device_mem_info is None返回 False、同一free_mem下 per-expert 通过而 full-stacked 因 +1 shard 不通过、上报值远小于3*max_file_size时门限按上报值生效(验证单位换算);(2) AUTO 且预检通过时断言_load_from_fastsafetensor以解析后 mode 被调用;(3) 用patch.dict(sys.modules, ...)装一个不含AutoLoader的假模块,直接断言_fastsafetensors_capability_error非 None 且_resolve_fastsafetensors_mode返回(None, reason);(4) 补max_file_size=0边界。
- 建议:补四类用例:(1)
- loader 跨模块导入 database 私有函数,fastsafetensors 策略逻辑分散两层并重复决策 @
rtp_llm/model_loader/loader.py:25- 建议:把「常量 + 归一化 + 能力探测 + env 兼容 + 降级决策」收敛到单一公开模块(如
rtp_llm/model_loader/fastsafetensors_policy.py),以公开名导出常量与函数,loader、database 与契约测试共同调用;database 侧只保留「入参已归一化」的断言而不再重复决策与告警,utils/database.py专注AutoLoader构造与迭代适配。同时移除_load_from_fastsafetensor的stacked_moe_mode=None默认值改为必填,消除隐式读 env 的旁路。
- 建议:把「常量 + 归一化 + 能力探测 + env 兼容 + 降级决策」收敛到单一公开模块(如
- RTP 原先强制的 loader 调优值与加载进度移交上游,缺迁移对照与 breaking-changes 登记 @
docs/backend/server_arguments.md:252- 建议:在
docs/release/breaking-changes.md增加一节并从本小节链接:列出被移除的旧 RTP 强制值(2GiB 读缓冲、shm/nogds 联动、强制开启进度条),给出恢复旧行为的等价FASTSAFETENSORS_CONFIG_JSON片段与对应上游键名,注明示例键路径适用的 fastsafetensors 版本,并列出识别降级用的告警日志关键字与回滚手段(LOAD_METHOD=scratch),使性能对照与回滚有明确落点。
- 建议:在
P3
- _load_weight 中模式解析与降级告警块重复两份 @
rtp_llm/model_loader/loader.py:306- 建议:抽一个私有方法(例如
_resolve_and_log_fastsafetensors_mode())返回Optional[str]并在内部完成 warning,AUTO 与显式分支各调一次;stacked_moe_mode is None的哨兵语义也随之收敛到一处。
- 建议:抽一个私有方法(例如
- copy-out 允许集无条件放行全局 raw stacked key,上游查询口径未在仓内固化 @
rtp_llm/model_loader/loader.py:519- 建议:把该契约固化下来:在
_build_fastsafetensors_local_copyout_keys或_iter_fastsafetensors_weights处以 docstring + 断言写明上游对 dim0 拆分键的查询口径与切片起始约定(例如校验tensor.shape[0]与全局 expert 数一致,否则 fail-fast 而非静默按 0..N-1 映射),并显式记录跨 rank 非对称这一前提;同时补一条world_size>1/ EP>1 的加载回归用例,使「谓词只影响本地 copy-out、不影响集合通信参与」这一假设在默认平台上也被固化。
- 建议:把该契约固化下来:在
- UE8M0 broadcast shim 的适用性断言外移为对上游 AutoLoader 的假设 @
rtp_llm/utils/torch_patch.py:37- 建议:将断言可验证化:或对
_dist.scatter/_dist.all_gather一并加同样的 uint8 view 兼容包装(成本低、幂等),或在注释中记录所依赖的上游版本与已核对的交付函数(参照现有_TORCH_TESTED_MAJOR_MINOR的版本门禁做法),并补一个world_size>1的 UE8M0 加载用例作为回归护栏。
- 建议:将断言可验证化:或对
- 测试中存在冗余且更脆弱的断言,fake 模块隔离写法不统一 @
rtp_llm/utils/test/ckpt_database_test.py:249- 建议:删除
_base断言,保留 data_ptr 比对表达「已 clone」语义;删除 366-369 行的精确字符串断言,或改为从rtp_llm.utils.database导入常量作为期望值,保留json.loads结构断言。把_install_fake_fastsafetensors改为返回上下文管理器(内部用patch.dict(sys.modules, ...))由各用例with使用,与新文件写法统一;或至少给InstalledFastsafetensorsContractTest加 setUp 断言sys.modules.get("fastsafetensors")不是本文件构造的假模块,让泄漏立即失败而非降级为 skip。
- 建议:删除
- 未安装 fastsafetensors 的平台会打印「incompatible」误导性告警 @
rtp_llm/model_loader/loader.py:280- 建议:把「包未安装」与「包已安装但能力不足」两类原因分开:前者用 INFO 级并明确措辞为「该平台未安装 fastsafetensors,使用 scratch」,后者保留 WARNING;文档 214 行相应改为「缺少
AutoLoader或未安装该包时回落scratch」,使日志与文档对同一现象的描述一致。
- 建议:把「包未安装」与「包已安装但能力不足」两类原因分开:前者用 INFO 级并明确措辞为「该平台未安装 fastsafetensors,使用 scratch」,后者保留 WARNING;文档 214 行相应改为「缺少
Checklist Findings (16 fail / 48 total)
General Principles Checklist
- [6.1] Architecture — 依赖方向:无循环依赖/跨层惊喜 → issue
loader 跨模块导入 database 私有函数,fastsafetensors 策略逻辑分散两层并重复决策
loader.py:25-33 从rtp_llm.utils.database显式导入三个下划线私有函数(_apply_fastsafetensors_env_compat、_callable_accepts_keyword、_normalize_fastsafetensors_stacked_moe_mode),把 database 内部实现细节提升为跨模块契约。策略常量与工具落在utils/database.py,决策落在model_loader/loader.py,职责边界被切开:dim0_split_templates判定在 loader.py:458 与 database.py:414-425 各做一次并各自 warning;_apply_fastsafetensors_env_compat在两层各调一次(loader.py:292/323 与 database.py:411);_load_from_fastsafetensor(stacked_moe_mode=None)(loader.py:565)的默认值在生产路径不可达却 - [6.1] Architecture — 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全 → issue
UE8M0 broadcast shim 的适用性断言外移为对上游 AutoLoader 的假设
旧注释的依据是「ParallelLoader与 RTP 自己的PerExpertParallelLoader都把 UE8M0 传输走pg.broadcast」,后半段在仓内可逐行验证。改为「AutoLoaderroutes UE8M0 transfers throughpg.broadcast」(torch_patch.py:36-39)后,dim0 切分交付已完全由第三方包实现,而 shim 只包装了_dist.broadcast(torch_patch.py:68-77):若上游对 dim0 切片改用scatter/all_gather,多卡加载 DSv4-Flash 的 UE8M0 scale 会重新命中Float8_e8m0fnu不被 NCCL 支持的错误。同文件对 torch 版本有_TORCH_TESTED_MAJOR_MINOR门禁并会告警,对上游包版本却无等价约束;该假设目前只由 SM100 / cuda13 的 DSV4 多卡 perf 目标间接覆盖,默认平台无回归护栏。 - [6.1] Architecture — 分层边界:新概念在正确层级,不泄漏内部 → issue
loader 跨模块导入 database 私有函数,fastsafetensors 策略逻辑分散两层并重复决策
loader.py:25-33 从rtp_llm.utils.database显式导入三个下划线私有函数(_apply_fastsafetensors_env_compat、_callable_accepts_keyword、_normalize_fastsafetensors_stacked_moe_mode),把 database 内部实现细节提升为跨模块契约。策略常量与工具落在utils/database.py,决策落在model_loader/loader.py,职责边界被切开:dim0_split_templates判定在 loader.py:458 与 database.py:414-425 各做一次并各自 warning;_apply_fastsafetensors_env_compat在两层各调一次(loader.py:292/323 与 database.py:411);_load_from_fastsafetensor(stacked_moe_mode=None)(loader.py:565)的默认值在生产路径不可达却 - [6.1] Architecture — 可观测性:日志/指标/超时可操作、非噪声 → issue
未安装 fastsafetensors 的平台会打印「incompatible」误导性告警
_fastsafetensors_capability_error(loader.py:452-455)把ImportError也归入「能力错误」,AUTO 路径遂在 280-283 行打印「the installed fastsafetensors is incompatible: No module named 'fastsafetensors'」。但deps/requirements_torch_gpu_cuda12.txt与deps/requirements_rocm.txt均不含该包(仅 cuda12_9 / cuda13 / cuda13_arm 有),这些平台上「未安装」被表述为「已安装但不兼容」。文档 214 行同样写「Only a missingAutoLoaderfalls back toscratch」,未涵盖「包本身缺失」这一更常见的情形。 - [6.1] Architecture — 回滚路径:风险行为存在运维回滚手段 → issue
RTP 原先强制的 loader 调优值与加载进度移交上游,缺迁移对照与 breaking-changes 登记
改动前 RTP 在构造 loader 时强制传入bbuf_size_kb(2GiB 读缓冲)、use_shm=not use_nogds,并固定use_tqdm_on_load=True使加载进度始终可见;改动后 database.py:446-459 的loader_kwargs仅保留可选的local_copyout_filter/dim0_split_templates,全仓已无这些 kwarg,默认值完全由安装包配置决定,加载进度可能从「默认开启」变为「默认关闭」。文档仅以「The installed FastSafeTensors version defines the precise configuration defaults and precedence」(docs:219-220)概括,既未记录旧的强制值,也未标注示例 JSON 中base.copier_type/parallel.use_tqdm_on_load适用的 wheel 版本,运维照抄若键名不符会静默无效。本仓已有 `docs/release/brea - [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue
NOGDS 兼容 env 覆写发生在首次 import fastsafetensors 之后,且不覆盖 FASTSAFETENSORS_CONFIG
AUTO 路径先调_resolve_fastsafetensors_mode(loader.py:276),其内部_fastsafetensors_capability_error已执行from fastsafetensors import AutoLoader(loader.py:453),之后才在 292 行调_apply_fastsafetensors_env_compat();显式分支顺序相同(307 → 323)。旧实现通过nogds=/use_shm=显式 kwargs 传参,不依赖 import 顺序;若安装包在 import 期固化配置单例,FASTSAFETENSORS_NOGDS=1写入的FASTSAFETENSORS_CONFIG_JSON与实际生效 copier 可能不一致,而文档 231-233 承诺覆写发生在「memory preflight 或构造 AutoLoader 之前」。另_apply_fastsafetensors_env_compat(database.py:29-43)只覆写 `F - [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue
未安装 fastsafetensors 的平台会打印「incompatible」误导性告警
_fastsafetensors_capability_error(loader.py:452-455)把ImportError也归入「能力错误」,AUTO 路径遂在 280-283 行打印「the installed fastsafetensors is incompatible: No module named 'fastsafetensors'」。但deps/requirements_torch_gpu_cuda12.txt与deps/requirements_rocm.txt均不含该包(仅 cuda12_9 / cuda13 / cuda13_arm 有),这些平台上「未安装」被表述为「已安装但不兼容」。文档 214 行同样写「Only a missingAutoLoaderfalls back toscratch」,未涵盖「包本身缺失」这一更常见的情形。 - [6.1] Quality — PR description 说明动机与设计 → issue
RTP 原先强制的 loader 调优值与加载进度移交上游,缺迁移对照与 breaking-changes 登记
改动前 RTP 在构造 loader 时强制传入bbuf_size_kb(2GiB 读缓冲)、use_shm=not use_nogds,并固定use_tqdm_on_load=True使加载进度始终可见;改动后 database.py:446-459 的loader_kwargs仅保留可选的local_copyout_filter/dim0_split_templates,全仓已无这些 kwarg,默认值完全由安装包配置决定,加载进度可能从「默认开启」变为「默认关闭」。文档仅以「The installed FastSafeTensors version defines the precise configuration defaults and precedence」(docs:219-220)概括,既未记录旧的强制值,也未标注示例 JSON 中base.copier_type/parallel.use_tqdm_on_load适用的 wheel 版本,运维照抄若键名不符会静默无效。本仓已有 `docs/release/brea - [6.1] Software Engineering — DRY:重复非平凡逻辑被抽取或显式复用 → issue
测试中存在冗余且更脆弱的断言,fake 模块隔离写法不统一
:249-250 用result[0][1]._base is None判断张量已拷贝,_base为 torch 私有属性、跨版本无兼容承诺,而紧随的 :251-258 已用untyped_storage().data_ptr()比对证明脱离原 storage,属重复且更脆弱的断言。:366 以'{"loader":"base","base":{"copier_type":"nogds"}}'精确字符串断言环境变量,该字面量是 database.py:26 常量的复制,而 :370-373 已用json.loads结构化断言同一语义;生产侧改用json.dumps(...)或调整键序会行为等价却误报失败。另_install_fake_fastsafetensors(:27-33)直接改写sys.modules且自身无还原机制,与新文件的patch.dict上下文写法并存;InstalledFastsafetensorsContractTest无自身隔离,假模块泄漏会表现为静默 skip 而非失败。 - [6.1] Software Engineering — SRP:模块/类职责单一 → issue
loader 跨模块导入 database 私有函数,fastsafetensors 策略逻辑分散两层并重复决策
loader.py:25-33 从rtp_llm.utils.database显式导入三个下划线私有函数(_apply_fastsafetensors_env_compat、_callable_accepts_keyword、_normalize_fastsafetensors_stacked_moe_mode),把 database 内部实现细节提升为跨模块契约。策略常量与工具落在utils/database.py,决策落在model_loader/loader.py,职责边界被切开:dim0_split_templates判定在 loader.py:458 与 database.py:414-425 各做一次并各自 warning;_apply_fastsafetensors_env_compat在两层各调一次(loader.py:292/323 与 database.py:411);_load_from_fastsafetensor(stacked_moe_mode=None)(loader.py:565)的默认值在生产路径不可达却 - [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue
UE8M0 broadcast shim 的适用性断言外移为对上游 AutoLoader 的假设
旧注释的依据是「ParallelLoader与 RTP 自己的PerExpertParallelLoader都把 UE8M0 传输走pg.broadcast」,后半段在仓内可逐行验证。改为「AutoLoaderroutes UE8M0 transfers throughpg.broadcast」(torch_patch.py:36-39)后,dim0 切分交付已完全由第三方包实现,而 shim 只包装了_dist.broadcast(torch_patch.py:68-77):若上游对 dim0 切片改用scatter/all_gather,多卡加载 DSv4-Flash 的 UE8M0 scale 会重新命中Float8_e8m0fnu不被 NCCL 支持的错误。同文件对 torch 版本有_TORCH_TESTED_MAJOR_MINOR门禁并会告警,对上游包版本却无等价约束;该假设目前只由 SM100 / cuda13 的 DSV4 多卡 perf 目标间接覆盖,默认平台无回归护栏。 - [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue
UE8M0 broadcast shim 的适用性断言外移为对上游 AutoLoader 的假设
旧注释的依据是「ParallelLoader与 RTP 自己的PerExpertParallelLoader都把 UE8M0 传输走pg.broadcast」,后半段在仓内可逐行验证。改为「AutoLoaderroutes UE8M0 transfers throughpg.broadcast」(torch_patch.py:36-39)后,dim0 切分交付已完全由第三方包实现,而 shim 只包装了_dist.broadcast(torch_patch.py:68-77):若上游对 dim0 切片改用scatter/all_gather,多卡加载 DSv4-Flash 的 UE8M0 scale 会重新命中Float8_e8m0fnu不被 NCCL 支持的错误。同文件对 torch 版本有_TORCH_TESTED_MAJOR_MINOR门禁并会告警,对上游包版本却无等价约束;该假设目前只由 SM100 / cuda13 的 DSV4 多卡 perf 目标间接覆盖,默认平台无回归护栏。 - [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue
决定加载方式的 \_is\_memory\_enough\_for\_fastsafetensor 与 AUTO 成功主路径零覆盖
真正决定「走 fastsafetensors 还是 scratch」的_is_memory_enough_for_fastsafetensor(loader.py:330-394)本次新增stacked_moe_mode形参、改用 transient budget 并做 bytes→MB 换算,却无任何用例(:203 整体 MagicMock),device_mem_info is None → return False早退分支同样无覆盖。AUTO 是默认入口,但两条用例(:163 wrapper 不兼容、:186 内存不足)都固定失败退 scratch,缺「AUTO 且内存足够 → 以解析后 mode 调用_load_from_fastsafetensor」与「AUTO 降级 full-stacked 时预检必须以 full-stacked 预算调用」的断言。「AutoLoader 缺失 → scratch」这条核心承诺也被三层 mock 顶替(:122 假模块带 AutoLoader,:143 patch 掉 capability_error,:1
RTP-LLM Checklist
- [I] 代码质量 — 同一功能用统一工具函数 → issue
测试中存在冗余且更脆弱的断言,fake 模块隔离写法不统一
:249-250 用result[0][1]._base is None判断张量已拷贝,_base为 torch 私有属性、跨版本无兼容承诺,而紧随的 :251-258 已用untyped_storage().data_ptr()比对证明脱离原 storage,属重复且更脆弱的断言。:366 以'{"loader":"base","base":{"copier_type":"nogds"}}'精确字符串断言环境变量,该字面量是 database.py:26 常量的复制,而 :370-373 已用json.loads结构化断言同一语义;生产侧改用json.dumps(...)或调整键序会行为等价却误报失败。另_install_fake_fastsafetensors(:27-33)直接改写sys.modules且自身无还原机制,与新文件的patch.dict上下文写法并存;InstalledFastsafetensorsContractTest无自身隔离,假模块泄漏会表现为静默 skip 而非失败。
Python Static-First Checklist
- [P.B] 错误处理 — 禁止 bare except 或静默吞异常 → issue
两处能力探测的异常集合不一致,AttributeError 会使「包过旧不致启动失败」的承诺失效
_fastsafetensors_capability_error的from fastsafetensors import AutoLoader只捕获(ImportError, OSError, RuntimeError)(loader.py:454),而同文件_fastsafetensors_transient_budget_bytes捕获(ImportError, AttributeError, OSError, TypeError, ValueError)(loader.py:434)。若安装包在 import 期抛AttributeError(常见于上游模块级初始化与新 torch 不兼容),异常会穿透_resolve_fastsafetensors_mode直接打挂加载,_load_weight无兜底,与文档 216 行「package age alone does not fail model startup」及本 PR 核心承诺不符。此外该分支把OSError/RuntimeError一律翻译成「能力缺 - [P.G] 测试规范 — mock/fake/stub 不得替代本次声称覆盖的生产边界 → issue
测试中存在冗余且更脆弱的断言,fake 模块隔离写法不统一
:249-250 用result[0][1]._base is None判断张量已拷贝,_base为 torch 私有属性、跨版本无兼容承诺,而紧随的 :251-258 已用untyped_storage().data_ptr()比对证明脱离原 storage,属重复且更脆弱的断言。:366 以'{"loader":"base","base":{"copier_type":"nogds"}}'精确字符串断言环境变量,该字面量是 database.py:26 常量的复制,而 :370-373 已用json.loads结构化断言同一语义;生产侧改用json.dumps(...)或调整键序会行为等价却误报失败。另_install_fake_fastsafetensors(:27-33)直接改写sys.modules且自身无还原机制,与新文件的patch.dict上下文写法并存;InstalledFastsafetensorsContractTest无自身隔离,假模块泄漏会表现为静默 skip 而非失败。
Strengths
- 降级链分级完整且可与代码逐条对应:缺
AutoLoader→ scratch(loader.py:478-479)、缺dim0_split_templates→ full-stacked(loader.py:480-486、database.py:414-425)、缺local_copyout_filter→ 全量物化 + RTP 消费端过滤(database.py:447-454),AUTO 与显式LOAD_METHOD两条入口均覆盖,wheel 版本差异不再像旧实现那样直接抛RuntimeError打挂启动。 - 用
inspect.signature能力探测替换硬版本号前缀校验,契约从「版本号字符串」收敛到「实际关键字」,对上游打包与回合策略的鲁棒性显著提升。 _build_fastsafetensors_local_copyout_keys(loader.py:508-519)同时保留展开后的 per-expert key 与 stacked 原始 key,与MoeAtomicWeight.get_tensor_names(ffn_weight.py:706-720)只返回本 rankselected_experts的语义对齐;消费循环 loader.py:617-618 的key not in tensor_to_weight_map: continue为老 wheel 缺local_copyout_filter时提供可靠兜底。_fastsafetensors_transient_budget_bytes对上报值做了bool/ 非数值 / 非有限 / 非正的完整拒绝并回落 legacy 估算(loader.py:414-431),边界值 None/0/-1/"8192"/inf/True 在用例中逐一枚举;full-stacked 额外叠加一个 max shard(loader.py:440-445)把兼容路径峰值纳入auto预检。_iter_fastsafetensors_weights(database.py:87-110)对每个 expert 切片显式clone()并注明「loader 可能在迭代推进后释放 batch buffer」,测试用untyped_storage().data_ptr()比对把该所有权不变量固化为可回归断言。test_env_compat_is_applied_before_auto_memory_preflight用 events 列表断言["env", "memory"],把隐式时序约束固化为可回归断言,且 patch 打在使用处符合 P.P.G.1。stacked_moe_mode在 AUTO 解析结果 → 显存预检 →_load_from_fastsafetensor→fastsafetensors_weights_iterator全链路显式参数传递,而非各层重复读环境变量;空值/纯空白回落默认(database.py:59-63),非法值在两侧都有带match的拒绝用例。- 新增 py_test 为 CPU-only(
env = {"CUDA_VISIBLE_DEVICES": ""}、无 GPU tag /exec_properties),与同目录test_per_channel_fp8_helpers/test_pure_tp_preshard约定一致,不额外占用 H20 资源。 - 文档主动披露
FASTSAFETENSORS_NOGDS的进程级副作用,并主动划清 RTP 与上游的配置归属(bucket/copier/queue/producer/进度/顺序归上游,仅 rank-local copy-out 归 RTP),避免 RTP 再造重复配置面。
| 4096, FASTSAFETENSORS_STACKED_MOE_MODE_FULL_STACKED | ||
| ), | ||
| 12 * 1024, | ||
| ) |
There was a problem hiding this comment.
📍 实际位置 rtp_llm/model_loader/test/test_fastsafetensors_loader_policy.py:299(不在 diff 展示范围内,就近挂载)
[P1] 新增 py_test 缺少 unittest.main(),15 条策略用例全部空跑并报 PASS
新文件在 299 行结束时无 if __name__ == "__main__": unittest.main()。test/BUILD:5-10 声明 py_test,tools/build_rules/prelude_bazel:11-12 的同名覆写仅注入 gpu_count 后仍委托 native.py_test,不改 main 处理,故源文件以 __main__ 直接执行:模块只完成 import 与两个 TestCase(TestFastsafetensorsLoaderPolicy 11 个 + TestFastsafetensorsTransientBudget 4 个,共 15 个 test 方法)的定义即以 0 退出,target 报 PASSED 而 0 条用例执行。同目录另外 7 个 py_test 源文件全部带该入口(test_stacked_moe_weight.py:484、test_pure_tp_preshard.py:188、test_per_channel_fp8_helpers.py:128 等)。后...
建议: 在文件末尾补 if __name__ == "__main__": + unittest.main(),与同目录既有约定一致;补齐后实跑该 target 并确认输出为 Ran 15 tests 而非 Ran 0 tests(静态走查未发现用例自身有断言错误)。建议同时全仓排查是否还有其他 py_test 源文件存在同类缺失;若希望长期免疫,可在 BUILD 层统一改用带 runner 的宏而非裸 py_test。
| | `--loader_recycle_handles` | ROCm + safetensors only: close consumed main-model shard handles to release mmap memory. Requires layer-numbered tensors and copies safetensors data out before closing; no effect on fastsafetensors, ViT, EPLB, or .bin weights. (`LOADER_RECYCLE_HANDLES`) | True | | ||
| | `--moe_pure_tp_preshard` | Disabled by default. Set true to pre-shard supported Qwen3-Next / Qwen3.5 MoE and offline FP8 weights under pure TP (`tp>1, dp=1, ep=1`) before device copy. Unsupported sources or layouts warn and use legacy full reads. (`MOE_PURE_TP_PRESHARD`) | False | | ||
|
|
||
| ### FastSafeTensors loader configuration |
There was a problem hiding this comment.
[P2] 默认 cuda12_9 wheel pin 未随新能力契约对齐,静默降级在 CI 与线上均不可观测
新实现依赖 AutoLoader 及 dim0_split_templates / local_copyout_filter / estimated_peak_device_bytes,被删文件原以 _REQUIRED_FST_VERSION="0.1.19" 硬门禁保证 per-expert 路径,现改为 warning 后静默降级(loader.py:280-291、311-322)。但 deps/requirements_torch_gpu_cuda12_9.txt:6 与其 lock 仍锁 fastsafetensors-0.1.19rc5+ali,cuda13 / cuda13_arm 已为 0.1.20+ali,本 PR 未 bump;文档 209-216 承诺完整能力集走 bounded per-expert 却全篇无最低版本。唯一触达真实 wheel 的 InstalledFastsafetensorsContractTest 逐级 skipTest,而全部 13 个 `--load_method fastsafetensors...
建议: 建议三项一起落地:一是文档补「能力 → 最低 fastsafetensors 版本 → 实际生效路径」矩阵(示例中的 parallel.use_tqdm_on_load 也应标注起始版本),并同步 bump deps/requirements_torch_gpu_cuda12_9.txt 及其 lock,使默认平台确实具备 per-expert 能力;二是让安装态契约测试在「应当支持」的镜像中 fail 而非 skip(例如以环境变量声明期望能力档位,未达到即断言失败),使降级在 CI 立即暴露;三是在加载入口固定打印一条 INFO 输出最终生效的 mode 与降级原因,或上报可告警的降级指标,使线上可自查。
| ) | ||
| budget = legacy_budget | ||
| else: | ||
| budget = int(estimate) |
There was a problem hiding this comment.
[P2] 显存预算完全信任上游上报值且无下界,AUTO 准入可能由回退 scratch 变为加载期 OOM
原判据为 (free_mem - model_mem) > 3 * max_file_mem,与 checkpoint 分片大小挂钩、自带经验余量。改后只要拿到有限正值即整体替换(loader.py:433 budget = int(estimate)),max_file_size 在该分支完全不参与计算,且无任何下界夹取;loader.py:387 仍以严格大于判定。预算只覆盖 loader 自有 buffer,未为 RTP 侧临时占用留余量:collector 累积的 per-expert clone、weight.load() 拼接、inline FP8 量化中间张量、非源 rank 的广播接收缓冲。测试侧 test_uses_positive_configured_bounded_peak 反把「任意有限正数一律信任、可下调安全余量」固化为期望行为(8192 < legacy 12288),却无任何用例约束下界:若上游回归上报极小正值(如 1),AUTO 会在本应退回 scratch 的场景选中 fastsafetensors。
建议: 建议保留下界或余量,例如 budget = max(int(estimate), legacy_budget) 或与 model_mem 成比例的经验值,并在日志中同时打印 estimate 与最终采用的 budget,便于线上区分「包上报值生效」与「走 legacy 估算」。测试侧二选一:补一条极小正值被夹到 legacy 下界的用例,或在用例名与注释中写明「无条件信任上报值」这一取舍及 OOM 风险,避免后续读者误判为疏漏。
| @@ -275,13 +302,35 @@ def _load_weight(self, device: str): | |||
| ) | |||
|
|
|||
| if load_method.lower() == LoadMethod.FASTSAFETENSORS: | |||
There was a problem hiding this comment.
[P2] 显式 LOAD_METHOD=fastsafetensors 路径不做内存预检,降级为 full-stacked 时无预算兜底
删除 per_expert_parallel_loader.py 后,per-expert 有界交付完全依赖安装包提供 dim0_split_templates;在原先能工作的 wheel 上,同一部署会静默降级为 full-stacked(database.py:414-425),峰值多出一份完整 stacked tensor。AUTO 路径尚可因 budget += max_file_size(loader.py:440-445)回退 scratch,但显式 LOAD_METHOD=fastsafetensors 分支(loader.py:304-324)完全不调用 _is_memory_enough_for_fastsafetensor(全仓仅 loader.py:293 一处调用),即使检测到 per-expert 不可用、需要多出一整个 shard 的显存,也会只打一条 warning 后直接进入 full-stacked 加载。
建议: 为显式 LOAD_METHOD=fastsafetensors 且发生 per-expert → full-stacked 降级的场景也执行一次内存预检,或至少把 warning 升级为带 max_file_size 与可用显存数值的显式提示,并在文档中明确「旧 wheel 上请改用 LOAD_METHOD=scratch」,使显式路径与 AUTO 路径的风险兜底口径一致。
| # RTP-LLM's ``PerExpertParallelLoader`` route every UE8M0 transfer | ||
| # through ``pg.broadcast`` (dim=-1 in fastsafetensors / per-expert | ||
| # broadcast in PerExpertParallelLoader); only the broadcast wrapper is | ||
| # not exercised by fastsafetensors -- ``AutoLoader`` routes UE8M0 |
There was a problem hiding this comment.
[P3] UE8M0 broadcast shim 的适用性断言外移为对上游 AutoLoader 的假设
旧注释的依据是「ParallelLoader 与 RTP 自己的 PerExpertParallelLoader 都把 UE8M0 传输走 pg.broadcast」,后半段在仓内可逐行验证。改为「AutoLoader routes UE8M0 transfers through pg.broadcast」(torch_patch.py:36-39)后,dim0 切分交付已完全由第三方包实现,而 shim 只包装了 _dist.broadcast(torch_patch.py:68-77):若上游对 dim0 切片改用 scatter / all_gather,多卡加载 DSv4-Flash 的 UE8M0 scale 会重新命中 Float8_e8m0fnu 不被 NCCL 支持的错误。同文件对 torch 版本有 _TORCH_TESTED_MAJOR_MINOR 门禁并会告警,对上游包版本却无等价约束;该假设目前只由 SM100 / cuda13 的 DSV4 多卡 perf 目标间接覆盖,默认平台无回归护栏。
建议: 将断言可验证化:或对 _dist.scatter / _dist.all_gather 一并加同样的 uint8 view 兼容包装(成本低、幂等),或在注释中记录所依赖的上游版本与已核对的交付函数(参照现有 _TORCH_TESTED_MAJOR_MINOR 的版本门禁做法),并补一个 world_size>1 的 UE8M0 加载用例作为回归护栏。
Checklist: [6.1] 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全;[6.1] 分布式/跨平台变更有对应覆盖;[6.1] 新逻辑有聚焦单测 + 相关集成/smoke 测试
9c76e93 to
22833c8
Compare
PR #1354 第 23 轮评审 — BLOCKED(3)
阻塞项(P0/P1,support≥3 且过全部门)[P1] 测试导入不存在的 FastSafeTensorsCompatibilityError,导致 ImportError
[P1] cu130 与 cu129 的 x86_64 wheel 使用相同 SHA256 hash,疑似复制粘贴错误导致 cuda13 构建哈希校验失败
[P1] cu129 与 cu130 的 fastsafetensors x86_64 wheel 在 whl_deps 中使用相同文件名与 SHA256,新位置复制粘贴疑点
非阻塞发现(并集报告:单票也保留,降级不丢弃)
已确认修复(recheck)
本轮 KPI{
"reps": 5,
"agentic_reps": 2,
"diff_truncated": false,
"patch_bytes": 62791,
"coverage": 1.0,
"shards": 1,
"discover_ok": 5,
"discover_fail": 0,
"discover_attempts": 6,
"clusters": 6,
"synth_fail": 0,
"confirm": {
"candidates": 0,
"to_block": 0,
"confirm_fail": 0
},
"blocking_dedup_merged": 0,
"fact_check": {
"checked": 1,
"hold": 1,
"refuted": 0,
"parse_fail": 0
},
"model": "whale/DeepSeek-V4-Pro-0813",
"round_index": 23,
"delta_files": 8,
"full_files": 23,
"new_findings": 2,
"merged": 4,
"recheck": {
"fixed": 3,
"still_open": 35,
"cannot_substantiate": 3,
"fail": 0
},
"blocking": 3,
"blocking_on_unchanged": 0
}本报告由 rtp-llm-agent-platform 自动生成,同一 PR 的后续轮次就地更新同一条评论。 rtp-llm-agent-platform review · 第 23 轮 · head |
22833c8 to
8412cd6
Compare
LLLLKKKK
left a comment
There was a problem hiding this comment.
AI Code Review - PR #1354
Status: BLOCKING
Summary: P0/0 · P1/1 · P2/12 · P3/6
Reviewed: commit 8412cd692ff4 · 2026-08-31 19:57 UTC+8
Blocking Issues
P1
- get_tensor_names 新增的 has_tensor 探测在含 {expert_id} 的模板上抛 KeyError,新增单测必然失败 @
rtp_llm/model_loader/ffn_weight.py:712- 建议:不要用
tensor_name()探测 stacked key:改为self._ckpt_name(self.weights[0], layer_id, selected_experts[0]),或新增stacked_tensor_name(layer_id)(内部用format_map配合缺省值),使含{expert_id}的模板不抛异常;ffn_weight.py:416-417、:512-513、loader.py:529的同类调用应一并加固。同时明确该回退服务哪种 checkpoint:若确为 per-expert 键,用例应断言「模板含{expert_id}时探测键与has_tensor实参符合预期」;若该组合不存在,则改用真实 stacked 模板(如layers.{i}.mlp.experts.gate_up_proj)配合has_tensor=False表达同一场景。
- 建议:不要用
Non-blocking Suggestions
P2
- has_tensor 门控只改命名侧,加载侧未同步导致回退端到端不成立 @
rtp_llm/model_loader/ffn_weight.py:424- 建议:把「是否使用逻辑 per-expert 键」抽成
MoeAtomicWeight的单一方法(如_use_stacked_expert_keys(layer_id, source_or_database)),令get_tensor_names、_load_raw_tensor、_load_pure_tp、per_channel_fp8_quant_weight._load_moe_inline_quant共用;_build_stacked_key_config(loader.py:526-531额外逐 idx 过滤非 identitymerge_fun)也应复用同一 helper,避免四处口径漂移。并补一条经weight.load(tensor_source=collector)或 database 回退路径的端到端用例,确认 collector 目标键与实际请求键一致;若该组合在生产不存在,则改为 fail-fast 并在文档说明,而不是留一个半截回退。
- 建议:把「是否使用逻辑 per-expert 键」抽成
- 显存预算完全信任上游估算且无下界,AUTO 准入可能由回退 scratch 变为加载期 OOM @
rtp_llm/model_loader/loader.py:421- 建议:保留保守下限,例如
budget = max(int(estimate), max_file_size),或为 per-expert 也追加一份 RTP 侧临时量(单 expert 切片 × 并发度);并补一条「上游 estimate 远小于 3×max_file 时 per-expert 不被过度放宽」的边界用例。顺带修正:422-428的日志:upstream_estimate_bytes与final_budget_bytes打印的是同一个budget,full-stacked 在:440叠加后final已失真,建议只在此处打印上游估算、把最终预算移到return之前统一输出。
- 建议:保留保守下限,例如
- 预算估算与能力探测的异常捕获集合不一致,RuntimeError/KeyError 会中断启动 @
rtp_llm/model_loader/loader.py:429- 建议:统一两处异常语义:按 docstring 的兜底承诺,至少补齐
RuntimeError、KeyError,与_fastsafetensors_capability_error对齐;若确实希望非法配置 fail-fast,则应在配置解析入口显式校验并给出可操作错误信息,而不是让异常从预算估算路径逃逸。
- 建议:统一两处异常语义:按 docstring 的兜底承诺,至少补齐
- 显存预检未覆盖全量物化档,显式请求 full-stacked 时完全不预检,额外分片对稠密模型无条件叠加 @
rtp_llm/model_loader/loader.py:435- 建议:把
local_copyout_filter可用性纳入_resolve_fastsafetensors_mode的返回信息,并在该档为预算追加保守余量或直接降级 scratch;显式路径改为「只要effective_mode == full-stacked就走预检」而非只在被动降级时;full-stacked的额外分片改为仅在存在 stacked MoE key 时叠加——可将 stacked key 存在性判定提前到预检之前,或让预算函数接收该布尔量。
- 建议:把
- wheel 能力档缺可失败门禁,cuda12_9 pin 未同步,降级在 CI 中表现为跳过 @
rtp_llm/utils/test/ckpt_database_test.py:413- 建议:引入由环境变量或 Bazel
env控制的期望档位(如RTP_LLM_EXPECT_FASTSAFETENSORS_TIER=per-expert):设置时把上述skipTest改为self.fail(...),未设置时保持 skip 以兼容开发机,并在需要最高档位的 CI/镜像目标上开启;同步升级 cuda12_9 的 wheel pin 与 lock,并在文档写明各 pinned 平台当前实际档位与该校验入口。对package-import-failed这类非「未安装」原因输出 error 级日志或提供 fail-fast 开关。
- 建议:引入由环境变量或 Bazel
- 文档「只有缺少 AutoLoader 才回落 scratch」与实现的多条 scratch 路径矛盾 @
docs/backend/server_arguments.md:214- 建议:改为枚举式表述:包未安装;包存在但导入失败(ABI/native helper);AUTO 的 safetensor/convert_device/重名前置门槛不满足;显存预检不足(
auto与显式降级路径均适用)。并给出可 grep 的日志关键字(falls back to scratch、exceeds the memory preflight budget、enough=False),把「extra shard 计入auto内存预算」改写为「计入 fastsafetensors 内存预检」。若希望显式请求保持 fail-fast,则应改为抛错而非静默降级,并同步文档这一取舍。
- 建议:改为枚举式表述:包未安装;包存在但导入失败(ABI/native helper);AUTO 的 safetensor/convert_device/重名前置门槛不满足;显存预检不足(
- 未说明 FASTSAFETENSORS_CONFIG_JSON 会反向决定 auto 是否准入 FastSafeTensors @
docs/backend/server_arguments.md:230- 建议:在该段补一句显式提示:
auto的显存预检预算由已安装 wheel 的 FastSafeTensors 配置推导(读取estimated_peak_device_bytes,缺失或非法时退回三倍最大分片的历史估算),因此调大 buffer/queue/producer 可能使auto改选scratch;并指明可通过启动日志中的transient_mem与enough字段确认实际判定结果。
- 建议:在该段补一句显式提示:
- loader 跨模块导入 database 私有符号,能力探测重复两份,模式解析带全局 env 写副作用 @
rtp_llm/model_loader/loader.py:25- 建议:把两个模式常量与三个 helper 下沉到公开模块(如
rtp_llm/model_loader/fastsafetensors_policy.py),由 loader 与 database 共同依赖;能力探测与降级只保留一处,由 loader 解析出的effective_mode单向传给 database;把 env 兼容写入从探测中剥离,只在真正构造AutoLoader之前执行一次,使探测保持无副作用;并把 AUTO 与显式两段「解析→预检→回落 scratch」逻辑收敛为一个私有方法。
- 建议:把两个模式常量与三个 helper 下沉到公开模块(如
- 随 EP rank 变化的 local_copyout_filter 交给使用集合通信的 loader,且无多 rank 覆盖 @
rtp_llm/model_loader/loader.py:642- 建议:在仓库内固化该上游契约(在
_build_fastsafetensors_local_copyout_keysdocstring 或 breaking-changes 中写清「filter 只影响本地 copy-out,不改变集合通信参与」,并说明它同时承载 checkpoint 键与逻辑 per-expert 键两类输入);补一个 EP>1 / TP>1 的多卡加载 smoke 或多进程测试;契约确认前可先对各 rank 的 key 集合做 hash all_gather 一致性校验,不一致时告警。
- 建议:在仓库内固化该上游契约(在
- 降级日志契约零覆盖,且 scratch 回退分支不输出文档承诺的结构化字段 @
rtp_llm/model_loader/test/test_fastsafetensors_loader_policy.py:192- 建议:补一个直连
_resolve_and_log_fastsafetensors_mode的用例:mock_resolve_fastsafetensors_mode分别返回(None, "package-not-installed: ...")、(full-stacked, "...missing dim0_split_templates")、(per-expert, None),用assertLogs断言 INFO/WARNING/INFO 并校验三个字段名出现;并在 scratch 回退与显存回退两条日志补齐同一组结构化字段,使日志与文档形成单一可 grep 契约。
- 建议:补一个直连
- AUTO 两条决策分支与 _build_stacked_key_config 的 database 接线无断言 @
rtp_llm/model_loader/test/test_fastsafetensors_loader_policy.py:72- 建议:在
:77之后补loader._build_stacked_key_config.assert_called_once_with([weight_info], database)(或改用patch.object(..., wraps=...)保留真实逻辑并给database.has_tensor配置返回值),并对fastsafetensors_weights_iterator的stacked_key_configkwarg 加断言;再补两个用例:is_safetensor=False(或convert_device=="cpu"、张量重名)时断言直接_load_from_scratch且_resolve_and_log_fastsafetensors_mode未被调用;AUTO 下_is_memory_enough_for_fastsafetensor返回 False 时断言落到 scratch 且_load_from_fastsafetensor未被调用。
- 建议:在
- stacked 判定的正向命中与混合 checkpoint 分支无覆盖 @
rtp_llm/model_loader/test/test_stacked_moe_weight.py:330- 建议:补两个 CPU 用例:一是
has_tensor返回True时断言注册出预期{expert_id}模板、且get_tensor_names走_get_expert_weights()分支;二是用side_effect让has_tensor只对model.layers.13.moe.w1命中,断言结果只含 w1 模板、w2 退回 per-expert ckpt 名,锁定混合 checkpoint 下两条读取路径可共存。
- 建议:补两个 CPU 用例:一是
P3
- 纯 CPU 逻辑用例落在 H20 独占目标,且测试类名与被测对象不符 @
rtp_llm/model_loader/test/test_stacked_moe_weight.py:237- 建议:把这两个不依赖 GPU 的用例移到
test_fastsafetensors_loader_policy.py或新建 CPU-only 目标,使门控在所有平台 lane 回归,test_stacked_moe_weight仅保留真正需要torch.cuda的TestGpuPreallocate;把get_tensor_names用例移入独立类(如TestMoeAtomicWeightTensorNames)并让用例名体现被测方法;删除冗余assertNotIn。
- 建议:把这两个不依赖 GPU 的用例移到
- 三个用例设置了永不生效的 _fastsafetensors_stacked_moe_mode mock @
rtp_llm/model_loader/test/test_fastsafetensors_loader_policy.py:189- 建议:删除这三处无效 mock;若确实想覆盖 env → 模式 → 决策的完整链路,应改为只 mock 更下层的
_fastsafetensors_capability_error并用patch.dict(os.environ, ...)驱动,让_resolve_and_log_fastsafetensors_mode真实执行,从而顺带补上该方法的日志契约覆盖。
- 建议:删除这三处无效 mock;若确实想覆盖 env → 模式 → 决策的完整链路,应改为只 mock 更下层的
- copy-out 允许集在 per-expert 模式下仍无条件放行 raw stacked key @
rtp_llm/model_loader/loader.py:552- 建议:按模式构造允许集:per-expert 下只放行 per-expert 逻辑键,full-stacked 才并入 raw key;若上游确实要求两种模式都放行 raw key,则在 docstring 中写明该上游契约与其对峰值显存的影响,并补一条按模式断言允许集内容的用例。
- 文档承诺的降级日志字段与 full-stacked 告警在关键场景不成立 @
docs/backend/server_arguments.md:226- 建议:把措辞收敛为「降级但仍可用时输出
requested_mode/effective_mode/degraded_reason;回退 scratch 时输出含falls back to scratch的日志(包未安装为 info,其余为 warning)」;把 full-stacked 告警限定为「checkpoint 存在 stacked MoE 权重时」,并说明显式请求(info)与被动降级(warning 且带degraded_reason)的级别差异。
- 建议:把措辞收敛为「降级但仍可用时输出
- 能力矩阵把两个独立的 keyword 探测描述成有序层级 @
docs/backend/server_arguments.md:219- 建议:把矩阵拆成两个正交维度(
local_copyout_filter决定 copy-out 范围,dim0_split_templates决定 stacked MoE 投递方式),或保留单表但补齐第四种组合行,并在第 2 行注明其 MoE 投递方式由dim0_split_templates单独决定。
- 建议:把矩阵拆成两个正交维度(
- UE8M0 broadcast shim 的适用性断言外移为对上游 AutoLoader 的假设 @
rtp_llm/utils/torch_patch.py:37- 建议:在注释中标注这是对上游行为的假设并给出当前验证依据(pinned wheel 的 backend 实现位置与已验证版本),或把假设改为可验证形式:同时包装
scatter(或对 UE8M0 走非 broadcast 集合时打 error 级日志),并在多卡 UE8M0 模型的 smoke 覆盖中加一次真实加载校验,使假设失效时有明确信号而非 NCCL 底层报错。
- 建议:在注释中标注这是对上游行为的假设并给出当前验证依据(pinned wheel 的 backend 实现位置与已验证版本),或把假设改为可验证形式:同时包装
Checklist Findings (15 fail / 48 total)
General Principles Checklist
- [6.1] Architecture — 依赖方向:无循环依赖/跨层惊喜 → issue
loader 跨模块导入 database 私有符号,能力探测重复两份,模式解析带全局 env 写副作用
loader.py:25-33从rtp_llm.utils.database导入三个下划线私有符号(_apply_fastsafetensors_env_compat、_callable_accepts_keyword、_normalize_fastsafetensors_stacked_moe_mode),把 database 内部实现细节提升为跨模块事实契约。dim0_split_templates能力探测在loader.py:455与database.py:419-430各写一遍并各自降级;local_copyout_filter探测只在 database 侧。此外语义为「解析/查询」的_resolve_fastsafetensors_mode在:471就地覆写进程级FASTSAFETENSORS_CONFIG_JSON,即使 AUTO 最终因显存不足选择 scratch,该覆写仍残留并影响同进程后续 loader(ViT/EPLB)。 - [6.1] Architecture — 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全 → issue
UE8M0 broadcast shim 的适用性断言外移为对上游 AutoLoader 的假设
注释由「ParallelLoader与 RTP 的PerExpertParallelLoader都只经pg.broadcast传输 UE8M0」改为「Scatter is not exercised by fastsafetensors --AutoLoaderroutes UE8M0 transfers throughpg.broadcast; only the broadcast wrapper is needed」。删除自有 loader(其_broadcast_per_expert确实只用pg.broadcast)后,该前提完全依赖上游实现,而 shim 只包了dist.broadcast(:68-77);若上游某档位改用 scatter/all_gather 传输 UE8M0 scale,多卡加载会重新命中 NCCL 不支持Float8_e8m0fnu的报错,仓内无任何测试或断言能提前发现,只有 torch 版本门(:54-65)做提示。 - [6.1] Architecture — 分层边界:新概念在正确层级,不泄漏内部 → issue
loader 跨模块导入 database 私有符号,能力探测重复两份,模式解析带全局 env 写副作用
loader.py:25-33从rtp_llm.utils.database导入三个下划线私有符号(_apply_fastsafetensors_env_compat、_callable_accepts_keyword、_normalize_fastsafetensors_stacked_moe_mode),把 database 内部实现细节提升为跨模块事实契约。dim0_split_templates能力探测在loader.py:455与database.py:419-430各写一遍并各自降级;local_copyout_filter探测只在 database 侧。此外语义为「解析/查询」的_resolve_fastsafetensors_mode在:471就地覆写进程级FASTSAFETENSORS_CONFIG_JSON,即使 AUTO 最终因显存不足选择 scratch,该覆写仍残留并影响同进程后续 loader(ViT/EPLB)。 - [6.1] Architecture — 可观测性:日志/指标/超时可操作、非噪声 → issue
文档承诺的降级日志字段与 full-stacked 告警在关键场景不成立
:226-228称 “RTP logs the requested mode, effective mode and degradation reason once at loader selection”,但只有「降级但仍可用」分支输出这三个字段(loader.py:502-510);回退 scratch 分支只输出自由文本且package-not-installed降为 info(:495-501),显存回退(:306-310)亦无字段。另:263-265称full-stacked“logs a warning”,实际该 warning 嵌在if stacked_key_config:内(:605-616),无 stacked MoE key 时不打印;用户显式请求 full-stacked 且 wheel 支持 dim0 时reason=None,日志走 info。运维以「必有 warning」巡检会漏判。 - [6.1] Architecture — 回滚路径:风险行为存在运维回滚手段 → issue
未说明 FASTSAFETENSORS_CONFIG_JSON 会反向决定 auto 是否准入 FastSafeTensors
:230-240把FASTSAFETENSORS_CONFIG_JSON/FASTSAFETENSORS_CONFIG描述为纯粹的上游后端与调优入口。但_fastsafetensors_transient_budget_bytes会读取load_config().estimated_peak_device_bytes(loader.py:398-401,docstring 明示该值为 queue/producer-aware batch-buffer 核算),并在:372-375用它替代原固定3 * max_file_size参与enough判定。运维调大 buffer/队列深度/producer 数会抬高transient_mem,可能让auto静默改选scratch,加载路径与耗时随之变化;按旧的「3 × max shard」心算容量也会与日志不符。 - [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue
显存预检未覆盖全量物化档,显式请求 full-stacked 时完全不预检,额外分片对稠密模型无条件叠加
预算只为 full-stacked 追加max_file_size。但能力矩阵还有一档「有AutoLoader、无local_copyout_filter→ 全量物化 + 消费端过滤」,database.py:452-459仅打 warning,_fastsafetensors_capability_error(:444-457)对该关键字完全不门控、预检也不加余量,而该档每 rank 物化全部张量。另一方向:显式路径的预检仅在capability_error is not None and requested==per-expert and effective==full-stacked时执行(:299-311);用户显式设RTP_FASTSAFETENSORS_STACKED_MOE_MODE=full-stacked时reason=None,高显存路径不做任何预检。且+max_file_size对不含 stacked MoE 的稠密模型也无条件叠加。 - [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue
copy-out 允许集在 per-expert 模式下仍无条件放行 raw stacked key
_build_fastsafetensors_local_copyout_keys无条件把stacked_key_config.keys()(原始 stacked key)并入允许集,docstring 给出的理由是「database 适配层在上游 yield 后才展开为 per-expert 键」——这只适用于 full-stacked 路径。per-expert 路径下上游已收到dim0_split_templates(database.py:460-464),若上游把该 raw key 视为「本 rank 需要完整张量」,则每 rank 仍会物化整张 stacked tensor,bounded 语义与无下界预算叠加后风险放大。该上游查询口径未在仓内固化,也无用例区分两种模式下的允许集。 - [6.1] Software Engineering — DRY:重复非平凡逻辑被抽取或显式复用 → issue
loader 跨模块导入 database 私有符号,能力探测重复两份,模式解析带全局 env 写副作用
loader.py:25-33从rtp_llm.utils.database导入三个下划线私有符号(_apply_fastsafetensors_env_compat、_callable_accepts_keyword、_normalize_fastsafetensors_stacked_moe_mode),把 database 内部实现细节提升为跨模块事实契约。dim0_split_templates能力探测在loader.py:455与database.py:419-430各写一遍并各自降级;local_copyout_filter探测只在 database 侧。此外语义为「解析/查询」的_resolve_fastsafetensors_mode在:471就地覆写进程级FASTSAFETENSORS_CONFIG_JSON,即使 AUTO 最终因显存不足选择 scratch,该覆写仍残留并影响同进程后续 loader(ViT/EPLB)。 - [6.1] Software Engineering — KISS/YAGNI:无投机性抽象 → issue
三个用例设置了永不生效的 _fastsafetensors_stacked_moe_mode mock
:189、:276、:294三个用例都设置了loader._fastsafetensors_stacked_moe_mode = MagicMock(...),但同时 mock 了_resolve_and_log_fastsafetensors_mode;而_load_weight(loader.py:263-316)自身从不调用_fastsafetensors_stacked_moe_mode,该方法只在_resolve_and_log_fastsafetensors_mode:492内部被调用。因此这三处 mock 是死代码,读者会误以为_load_weight直接读取该开关,掩盖了「请求模式由模式解析函数统一读取」的真实结构。 - [6.1] Software Engineering — SRP:模块/类职责单一 → issue
纯 CPU 逻辑用例落在 H20 独占目标,且测试类名与被测对象不符
test_missing_raw_stacked_tensor_uses_checkpoint_expert_keys(:237)与test_missing_raw_stacked_tensor_is_not_registered_for_dim0_split(:330)全程使用 MagicMock database、不触碰 GPU,却挂在带tags=["H20"]、exec_properties={'gpu':'H20'}的test_stacked_moe_weight(test/BUILD:12-20),只在 H20 lane 执行,导致 database 门控在 ROCm/ARM/PPU lane 无覆盖并白占稀缺配额——而本 PR 已新建 CPU-only 目标(BUILD:5-10)。另TestBuildStackedKeyConfigdocstring 只声明测_build_stacked_key_config,:237实际测MoeAtomicWeight.get_tensor_names;`:262-2 - [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue
UE8M0 broadcast shim 的适用性断言外移为对上游 AutoLoader 的假设
注释由「ParallelLoader与 RTP 的PerExpertParallelLoader都只经pg.broadcast传输 UE8M0」改为「Scatter is not exercised by fastsafetensors --AutoLoaderroutes UE8M0 transfers throughpg.broadcast; only the broadcast wrapper is needed」。删除自有 loader(其_broadcast_per_expert确实只用pg.broadcast)后,该前提完全依赖上游实现,而 shim 只包了dist.broadcast(:68-77);若上游某档位改用 scatter/all_gather 传输 UE8M0 scale,多卡加载会重新命中 NCCL 不支持Float8_e8m0fnu的报错,仓内无任何测试或断言能提前发现,只有 torch 版本门(:54-65)做提示。 - [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue
stacked 判定的正向命中与混合 checkpoint 分支无覆盖
两个新增用例都只断言database.has_tensor恒为False的行为。既有test_basic_mapping(:267-288)仍是_build_stacked_key_config([wi])单参调用、不走 database 过滤,因此「传入 database 且has_tensor命中 → 仍注册 dim0 split 模板」这一正向分支无任何用例;「w1 存在 stacked 张量而 w2 不存在」的混合 checkpoint(该门存在的真实动因,_PURE_TP_LAYOUTS确有(moe_w1, stack_moe_w1, 2)双 ckpt 张量布局)同样未覆盖。判定写反或 key 拼接偏移时现有用例仍全绿。 - [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue
纯 CPU 逻辑用例落在 H20 独占目标,且测试类名与被测对象不符
test_missing_raw_stacked_tensor_uses_checkpoint_expert_keys(:237)与test_missing_raw_stacked_tensor_is_not_registered_for_dim0_split(:330)全程使用 MagicMock database、不触碰 GPU,却挂在带tags=["H20"]、exec_properties={'gpu':'H20'}的test_stacked_moe_weight(test/BUILD:12-20),只在 H20 lane 执行,导致 database 门控在 ROCm/ARM/PPU lane 无覆盖并白占稀缺配额——而本 PR 已新建 CPU-only 目标(BUILD:5-10)。另TestBuildStackedKeyConfigdocstring 只声明测_build_stacked_key_config,:237实际测MoeAtomicWeight.get_tensor_names;`:262-2
RTP-LLM Checklist
- [I] 代码质量 — 同一功能用统一工具函数 → issue
loader 跨模块导入 database 私有符号,能力探测重复两份,模式解析带全局 env 写副作用
loader.py:25-33从rtp_llm.utils.database导入三个下划线私有符号(_apply_fastsafetensors_env_compat、_callable_accepts_keyword、_normalize_fastsafetensors_stacked_moe_mode),把 database 内部实现细节提升为跨模块事实契约。dim0_split_templates能力探测在loader.py:455与database.py:419-430各写一遍并各自降级;local_copyout_filter探测只在 database 侧。此外语义为「解析/查询」的_resolve_fastsafetensors_mode在:471就地覆写进程级FASTSAFETENSORS_CONFIG_JSON,即使 AUTO 最终因显存不足选择 scratch,该覆写仍残留并影响同进程后续 loader(ViT/EPLB)。
Python Static-First Checklist
- [P.G] 测试规范 — mock/fake/stub 不得替代本次声称覆盖的生产边界 → issue
AUTO 两条决策分支与 _build_stacked_key_config 的 database 接线无断言
本 PR 为_build_stacked_key_config新增database参数(loader.py:521,调用点:602-604),作用是过滤 checkpoint 中不存在的 raw stacked key(:530-531);但:72直接把该方法替换为固定返回值的 MagicMock 且无assert_called_once_with,调用点若回退成单参调用不会被发现。AUTO 分支四种结果中,只覆盖「能力不可用→scratch」(:179)与「能力可用且内存充足→fastsafetensors」(:245);前置门槛不满足(loader.py:285-286,本 PR 把has_module检查移交模式解析,属行为变更)与_is_memory_enough_for_fastsafetensor返回 False(:283-284)两条无覆盖。
Strengths
- 删除
per_expert_parallel_loader.py移除了对上游私有 API 与__version__前缀硬校验的耦合,改为基于签名的能力探测,是本 PR 最有价值的改动;全仓消费者搜索零命中,BUILD用 glob 无悬挂 srcs。 _callable_accepts_keyword(database.py:74-89)只接受POSITIONAL_OR_KEYWORD/KEYWORD_ONLY,明确拒绝把**kwargs当能力声明,避免兼容 wrapper 静默丢弃dim0_split_templates后 RTP 误判已启用 bounded 路径;test_kwargs_only_signature_is_not_treated_as_dim0_capability将其钉成契约。- 能力分级降级不让 wheel 版本致命:仅缺
AutoLoader回落 scratch,缺dim0_split_templates降级 full-stacked 并复算预检,package-not-installed由 warning 降为 info。 _fastsafetensors_transient_budget_bytes对上游估算做完整校验(bool单列、math.isfinite、<=0),test_invalid_estimates_use_three_max_files用subTest覆盖None/0/-1/"8192"/inf/True六种非法值,体现对bool是int子类这一陷阱的理解。_apply_fastsafetensors_env_compat()提前到_resolve_fastsafetensors_mode入口(loader.py:471),在能力探测 import 与显存预检之前生效,并由test_env_compat_is_applied_before_capability_import用__getattr__记录时序锁定。- 新增 CPU-only 目标
test_fastsafetensors_loader_policy(CUDA_VISIBLE_DEVICES="")把策略决策与真实 IO 解耦;test_full_stacked_extra_shard_changes_auto_admission用 free=2.5MiB/estimate=2MiB/max_file=1MiB 真实翻转 AUTO 准入结论。 _iter_fastsafetensors_weights(database.py:109-115)对每个 expert 切片显式clone()并注释说明 loader 可能释放 batch buffer;ckpt_database_test.py:250-257用untyped_storage().data_ptr()校验确实 clone 而非视图,守住张量所有权不变量。- 测试隔离设计正确:
FastsafetensorsAutoLoaderTest.setUp用patch.dict(sys.modules/os.environ)+addCleanup,InstalledFastsafetensorsContractTest.setUp主动断言未命中 fake 模块。 - 文档主动披露
FASTSAFETENSORS_NOGDS=1是进程级覆写且不还原,并给出full-stacked→LOAD_METHOD=scratch的两级运维回滚阶梯。
| @@ -706,8 +706,13 @@ def _load_raw_tensor_gpu_preallocate( | |||
| def get_tensor_names( | |||
There was a problem hiding this comment.
📍 实际位置 rtp_llm/model_loader/ffn_weight.py:424(不在 diff 展示范围内,就近挂载)
[P2] has_tensor 门控只改命名侧,加载侧未同步导致回退端到端不成立
get_tensor_names:709-716 现按 has_stacked_tensor(含 database.has_tensor)选 ckpt_weights,但 _load_raw_tensor:423-425 与 per_channel_fp8_quant_weight.py:815-817 仍是 _get_expert_weights() if stacked_ckpt_keys else weights,has_tensor 只决定是否包 StackSplitTensorSource。stacked 声明为真而 raw key 缺失时:collector 目标是 checkpoint 原始键,永不到达 → 走 loader.py:699-717 database 回退 → 消费侧请求逻辑键 layers.{i}.moe.{name}.{eid}.{idx} → CkptDatabase.load_tensor 返回 [](database.py:343)→ identity 抛 ts is empty(`...
建议: 把「是否使用逻辑 per-expert 键」抽成 MoeAtomicWeight 的单一方法(如 _use_stacked_expert_keys(layer_id, source_or_database)),令 get_tensor_names、_load_raw_tensor、_load_pure_tp、per_channel_fp8_quant_weight._load_moe_inline_quant 共用;_build_stacked_key_config(loader.py:526-531 额外逐 idx 过滤非 identity merge_fun)也应复用同一 helper,避免四处口径漂移。并补一条经 weight.load(tensor_source=collector) 或 database 回退路径的端到端用例,确认 collector 目标键与实际请求键一致;若该组合在生产不存在,则改为 fail-fast 并在文档说明,而不是留一个半截回退。
| ) | ||
| budget = legacy_budget | ||
| else: | ||
| budget = int(estimate) |
There was a problem hiding this comment.
[P2] 显存预算完全信任上游估算且无下界,AUTO 准入可能由回退 scratch 变为加载期 OOM
loader.py:420-428:estimated_peak_device_bytes 为正即 budget = int(estimate),日志明示「without applying the legacy floor」,仅 full-stacked 才 += max_file_size。但 per-expert 路径 RTP 侧仍有临时显存:_iter_fastsafetensors_weights 逐 expert clone()(database.py:115)、StackSplitTensorSource 缓存整张 stacked tensor(tensor_source.py:39-50)、inline FP8 量化中间量;上游估算只覆盖 loader 自有 buffer。而 enough = (free_mem - model_mem) > transient_mem(:375)是无余量严格不等式,AUTO 准入相较原 3 * max_file_size 明显放宽,紧显存机器可能从「回退 scratch」变为加载期 ...
建议: 保留保守下限,例如 budget = max(int(estimate), max_file_size),或为 per-expert 也追加一份 RTP 侧临时量(单 expert 切片 × 并发度);并补一条「上游 estimate 远小于 3×max_file 时 per-expert 不被过度放宽」的边界用例。顺带修正 :422-428 的日志:upstream_estimate_bytes 与 final_budget_bytes 打印的是同一个 budget,full-stacked 在 :440 叠加后 final 已失真,建议只在此处打印上游估算、把最终预算移到 return 之前统一输出。
| installed_module = sys.modules.get("fastsafetensors") | ||
| self.assertFalse(getattr(installed_module, "_rtp_test_fake", False)) | ||
|
|
||
| def test_available_auto_loader_contract_is_classified(self) -> None: |
There was a problem hiding this comment.
[P2] wheel 能力档缺可失败门禁,cuda12_9 pin 未同步,降级在 CI 中表现为跳过
test_available_auto_loader_contract_is_classified 在 :417/:421/:425/:429/:433 连续用 skipTest 处理「未安装/无 AutoLoader/无 local_copyout_filter/无 dim0_split_templates/无 load_config」五级降级,只有全能力档才真正断言,其余用例全用 fake AutoLoader。本 PR 未改任何依赖文件:deps/requirements_torch_gpu_cuda12_9.txt 及其 lock 仍钉 fastsafetensors 0.1.19rc5+ali,而 deps/requirements_torch_gpu_cuda13.txt、deps/requirements_cuda13_arm.txt、arch_config/arch_select.bzl 已是 0.1.20+ali,两个新 keyword 属新 API。结果是静默退化为 full-stacked 或全量物化时 CI 仍全绿,与文...
建议: 引入由环境变量或 Bazel env 控制的期望档位(如 RTP_LLM_EXPECT_FASTSAFETENSORS_TIER=per-expert):设置时把上述 skipTest 改为 self.fail(...),未设置时保持 skip 以兼容开发机,并在需要最高档位的 CI/镜像目标上开启;同步升级 cuda12_9 的 wheel pin 与 lock,并在文档写明各 pinned 平台当前实际档位与该校验入口。对 package-import-failed 这类非「未安装」原因输出 error 级日志或提供 fail-fast 开关。
| # RTP-LLM's ``PerExpertParallelLoader`` route every UE8M0 transfer | ||
| # through ``pg.broadcast`` (dim=-1 in fastsafetensors / per-expert | ||
| # broadcast in PerExpertParallelLoader); only the broadcast wrapper is | ||
| # not exercised by fastsafetensors -- ``AutoLoader`` routes UE8M0 |
There was a problem hiding this comment.
[P3] UE8M0 broadcast shim 的适用性断言外移为对上游 AutoLoader 的假设
注释由「ParallelLoader 与 RTP 的 PerExpertParallelLoader 都只经 pg.broadcast 传输 UE8M0」改为「Scatter is not exercised by fastsafetensors -- AutoLoader routes UE8M0 transfers through pg.broadcast; only the broadcast wrapper is needed」。删除自有 loader(其 _broadcast_per_expert 确实只用 pg.broadcast)后,该前提完全依赖上游实现,而 shim 只包了 dist.broadcast(:68-77);若上游某档位改用 scatter/all_gather 传输 UE8M0 scale,多卡加载会重新命中 NCCL 不支持 Float8_e8m0fnu 的报错,仓内无任何测试或断言能提前发现,只有 torch 版本门(:54-65)做提示。
建议: 在注释中标注这是对上游行为的假设并给出当前验证依据(pinned wheel 的 backend 实现位置与已验证版本),或把假设改为可验证形式:同时包装 scatter(或对 UE8M0 走非 broadcast 集合时打 error 级日志),并在多卡 UE8M0 模型的 smoke 覆盖中加一次真实加载校验,使假设失效时有明确信号而非 NCCL 底层报错。
Checklist: [6.1] 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全;[6.1] 分布式/跨平台变更有对应覆盖
8412cd6 to
4e91157
Compare
LLLLKKKK
left a comment
There was a problem hiding this comment.
AI Code Review - PR #1354
Status: BLOCKING
Summary: P0/2 · P1/0 · P2/11 · P3/7
Reviewed: commit 4e91157bc861 · 2026-08-31 21:27 UTC+8
Blocking Issues
P0
- stacked MoE 经 fastsafetensors 加载时 raw/逻辑键判定不一致,必然抛异常 @
rtp_llm/model_loader/ffn_weight.py:439- 建议:把「用哪套键命名」与「是否包装
StackSplitTensorSource」两个决策解耦,恢复改前的正确结构:ckpt_weights只依赖与get_tensor_names同源的静态布局/database 判据(例如bool(self._raw_stacked_tensor_names(layer_id))或self._has_raw_stacked_tensors(load_config.database, layer_id)),tensor_source.has_tensor探测仅用于决定是否包装。建议把这段「选键 + 是否包装」收敛为MoeAtomicWeight上的一个公共辅助方法,供ffn_weight.py与per_channel_fp8_quant_weight.py共用。同时补一个 CPU-only 回归用例:构造真实MoeAtomicWeight(stacked_ckpt_keys=True),用get_tensor_names产出的逻辑键建TensorCollector并逐 expertstore_tensor后调用weight.load,断言输出与DatabaseTensorSource路径一致,把这条生产不变量钉死。
- 建议:把「用哪套键命名」与「是否包装
- inline FP8 MoE 路径重复同一误判,现有 ROCm 用例会失败 @
rtp_llm/model_loader/per_channel_fp8_quant_weight.py:811- 建议:与上一条同源,请一并修复并复用同一个公共判据方法,避免同一逻辑在两处各自演化。修复后确认
//rtp_llm/model_loader/test:test_inline_fp8_quant的 ROCm 目标恢复通过,并把该 collector 场景补一份 CPU-only 变体(含has_prequantized_scale命中路径),使这条边界不再只靠 GPU 目标守护。
- 建议:与上一条同源,请一并修复并复用同一个公共判据方法,避免同一逻辑在两处各自演化。修复后确认
Non-blocking Suggestions
P2
- per-expert 模式把 raw stacked 键排除出 copy-out 白名单,依赖未经真 wheel 验证的上游过滤时机 @
rtp_llm/model_loader/loader.py:576- 建议:在该函数注释里把「上游先 split 后 filter」标注为显式契约假设,并把已安装 wheel 契约测试从「只校验签名」扩展到「同时装配
dim0_split_templates+local_copyout_filter时 raw 键不会被提前丢弃、stacked 张量仍被切分投递」;再补一例 FakeAutoLoader 声明了dim0_split_templates但iterate_weights仍返回 raw 键的交叉场景,断言期望行为。保守替代方案是 per-expert 也并入stacked_key_config.keys(),让过滤时机差异不再影响正确性。另建议给 loader.py:731 的兜底计数加 WARNING 阈值,让大面积静默回退可被告警发现。
- 建议:在该函数注释里把「上游先 split 后 filter」标注为显式契约假设,并把已安装 wheel 契约测试从「只校验签名」扩展到「同时装配
- 取消 3-shard 下限后,上游预估失真会把优雅降级变成加载期 OOM @
rtp_llm/model_loader/loader.py:425- 建议:为 per-expert 也保留一个可关闭的保守下限(例如
max(upstream_estimate, max_file_size))或可配安全系数;至少在采纳值明显小于单个 shard 时打 WARNING,并同时打印上游原值与最终采用值,把「上游可能低估」的风险前置到启动日志。同步在文档该段补一句双向说明:上游估计小于历史3 × max checkpoint shard时 AUTO 准入会比旧版更宽松,可通过fastsafetensor memory check的transient_mem字段核对实际生效预算。
- 建议:为 per-expert 也保留一个可关闭的保守下限(例如
- AutoLoader 能力探测重复两份,database 侧降级分支与已固定的 copy-out 键集合不自洽 @
rtp_llm/model_loader/loader.py:645- 建议:保留单一决策点:由
ModelLoader._resolve_fastsafetensors_mode决定最终 mode,fastsafetensors_weights_iterator只消费传入的stacked_moe_mode;若必须保留 database 侧防御,则应在降级时直接raise(或回传实际 mode 供上层重建键集合并重打告警),而不是在键集合已固定的情况下静默换路。
- 建议:保留单一决策点:由
_build_stacked_key_config的database=None默认分支语义相反且存在无效计算 @rtp_llm/model_loader/loader.py:540- 建议:把
database改为必填(或直接读self._load_config.database并去掉静态方法),删除易误用的None兜底并同步更新四处单参测试调用;把_raw_stacked_tensor_names的调用移到跳过判断之后并与_has_raw_stacked_tensors合并计算,使「是否登记 stacked 键」与get_tensor_names/_load_raw_tensor共享唯一判据来源。
- 建议:把
- 新增策略测试全量 mock,两个 P0 恰落在被 mock 的加载边界上 @
rtp_llm/model_loader/test/test_fastsafetensors_loader_policy.py:54- 建议:补一个 CPU-only 端到端用例:构造真实
MoeAtomicWeight(stacked_ckpt_keys=True),用get_tensor_names产出的键建TensorCollector,逐个逻辑 expert 键store_tensor后调用weight.load(collector, ...),断言输出 shape 与DatabaseTensorSource路径一致——该用例可直接把两个 P0 钉死,成本很低。再补一个闭包不变量用例:把_build_stacked_key_config的模板按range(expert_num)展开,断言覆盖全部被选 expert 且是get_tensor_names()的子集。
- 建议:补一个 CPU-only 端到端用例:构造真实
- 已安装 wheel 契约测试在其 target 中必然 skip,被删除的版本门禁无等价替代 @
rtp_llm/utils/test/ckpt_database_test.py:413- 建议:拆出一个能真正跑起来的 target(
srcs/main复用ckpt_database_test.py、args = ["InstalledFastsafetensorsContractTest"]),deps 追加 fastsafetensors requirement 别名(rtp_llm/models_py/standalone/BUILD:4有可复用范式,新增策略测试已通过py_standalone_testlib拿到该 wheel),并显式env = {"RTP_LLM_EXPECT_FASTSAFETENSORS_TIER": "per-expert"},使能力缺失按文档意图构成打包失败。若暂不打算强约束,请在文档中明确该守卫尚未接入 CI。
- 建议:拆出一个能真正跑起来的 target(
- tier 分类自行实现且与生产判据分叉,也不校验 AutoLoader 真实调用形状 @
rtp_llm/utils/test/ckpt_database_test.py:428- 建议:契约测试直接复用
_callable_accepts_keyword判定各关键字,使档位分类与生产降级同源;并补形状断言:校验__init__前两个位置参数的名称与顺序、device可作关键字传入、iterate_weights/close为可调用属性。若允许构造成本,可用一个最小 safetensors 文件加SingleGroup做一次真实构造 +iterate_weights()+close()冒烟。同时建议评估把TypeError纳入_fastsafetensors_capability_error的捕获集合,让上游签名漂移表现为降级而非启动崩溃。
- 建议:契约测试直接复用
- assertLogs 的 level 是捕获下限,「包缺失记 INFO」的分级承诺未被真正校验 @
rtp_llm/model_loader/test/test_fastsafetensors_loader_policy.py:438- 建议:改为断言精确级别,例如捕获后
self.assertEqual([r.levelname for r in logs.records], [level]),或对目标记录单独比较levelname,使级别回归可被 CI 发现。
- 建议:改为断言精确级别,例如捕获后
- 文档承诺的
falls back to scratch标记不覆盖内存预检与 AUTO 前置条件回退 @docs/backend/server_arguments.md:226- 建议:二选一并保持前后一致:(a)在 loader.py:284-289 与 :309-314 两条 warning 末尾补上同一标记,并为 AUTO 前置条件不满足分支补一条同格式日志,使该标记成为真正可 grep 的运维契约;或(b)把本文档与
docs/release/breaking-changes.md:29-30的该句收窄为「能力/包相关的 scratch 回退含该标记」,并显式列出memory-preflight-failed、full-stacked-memory-preflight-failed两个degraded_reason取值,说明前置条件不满足只能依据load method:日志判断。
- 建议:二选一并保持前后一致:(a)在 loader.py:284-289 与 :309-314 两条 warning 末尾补上同一标记,并为 AUTO 前置条件不满足分支补一条同格式日志,使该标记成为真正可 grep 的运维契约;或(b)把本文档与
- 文档称内存预检同样约束显式 fastsafetensors,实际默认 per-expert 完全跳过预检 @
docs/backend/server_arguments.md:216- 建议:按路径分别说明,不要把
auto的行为推广到显式路径:明确预检只在auto选路时、以及显式请求且生效模式为full-stacked时执行;显式LOAD_METHOD=fastsafetensors且生效模式为 per-expert 时出于尊重用户显式请求不做显存降级,因此不会出现fastsafetensor memory check日志,需要兜底请用auto或LOAD_METHOD=scratch。若产品意图本就是显式路径也要兜底,则应修改 loader.py:305-308 的短路条件而非仅改文档。
- 建议:按路径分别说明,不要把
- 文档要求「安装匹配的 wheel」却无版本锚点,且各平台 pin 版本不一致 @
docs/backend/server_arguments.md:228- 建议:在该段写出提供各档位(
per-expert/local_copyout_filter)所需的最低 fastsafetensors 版本,并说明当前各平台 pin 各自落在哪一档;若 cuda12_9 与 cuda13/arm 不同档,需显式点出平台差异及其显存影响,必要时同步 bump cuda12_9 的 pin。同时在需要 per-expert 的 CI 目标固定设置RTP_LLM_EXPECT_FASTSAFETENSORS_TIER,让版本漂移在构建/测试阶段暴露而非依赖人读文档。
- 建议:在该段写出提供各档位(
P3
- 跨模块导入下划线私有符号,通用反射工具放错层 @
rtp_llm/model_loader/loader.py:30- 建议:把 mode 常量与
_normalize_*/_apply_*收敛到一个独立的 fastsafetensors 策略模块(或去掉下划线成为该模块公开 API)由 loader 与 database 共同依赖;_callable_accepts_keyword移入rtp_llm/utils下的通用工具模块,并在_apply_fastsafetensors_env_compat的 docstring 中醒目标注其进程级副作用。MoeAtomicWeight侧建议把被三处复用的判据提升为公开方法(与 P0 的修复合并进行)。
- 建议:把 mode 常量与
- 假 fastsafetensors 模块直接改写全局 sys.modules,隔离方式脆弱 @
rtp_llm/utils/test/ckpt_database_test.py:27- 建议:把 helper 改为返回作用域精确的 patcher(内部
patch.dict(sys.modules, {"fastsafetensors": module}),调用方addCleanup(patcher.stop)),或让它接收 testcase 并自行addCleanup,使安装与还原成对出现;同时移除 setUp 中对整个sys.modules的快照式还原。
- 建议:把 helper 改为返回作用域精确的 patcher(内部
- tier 契约测试为精确相等断言,文档却表述为「仅更低档位会失败」 @
docs/backend/server_arguments.md:231- 建议:改为精确匹配语义的表述(「与期望值做精确断言,任何不一致(含实际高于期望)都会失败」),并补充:若只想做最低门槛校验,应把期望值设为实际部署的目标 tier,或在测试侧改为 tier 序关系断言。
- 取值边界与 per-expert 投递机制的文档表述不够精确 @
docs/backend/server_arguments.md:288- 建议:把 :288-290 改为「去除首尾空白后为空则取默认值;其余不等于
per-expert/full-stacked的取值抛 ValueError」。把 :262-264 改为描述意图与委托关系,例如「默认通过dim0_split_templates请求上游 AutoLoader 以有界显存逐 expert 投递,具体切片与 broadcast 时序由所安装的 fastsafetensors 版本决定」,避免把上游实现细节固化为 RTP 的对外承诺而随上游升级静默失真。
- 建议:把 :288-290 改为「去除首尾空白后为空则取默认值;其余不等于
- 能力表未说明缺失 local_copyout_filter 档位的显存代价,预算也未补偿 @
docs/backend/server_arguments.md:222- 建议:在能力表下方补一句:缺失
local_copyout_filter时所有 rank 会物化全部 tensor,峰值显存高于 rank-local copy-out 档位,且当前内存预检不为该档位追加预算;显存紧张的部署应优先升级 wheel 或改用LOAD_METHOD=scratch。
- 建议:在能力表下方补一句:缺失
_has_raw_stacked_tensors的多 ckpt weight「全存在」语义缺少边界覆盖 @rtp_llm/model_loader/test/test_fastsafetensors_loader_policy.py:477- 建议:补一个
weights=[CkptWeightInfo("...w_gate"), CkptWeightInfo("...w_up")]且has_tensor仅对其中之一返回 True 的用例,断言get_tensor_names回退到原始 ckpt 名而非逻辑 per-expert 名,并断言该 atomic weight 不会进入_build_stacked_key_config结果,把all()语义钉死。
- 建议:补一个
- UE8M0 broadcast shim 的适用性断言外移为对上游 AutoLoader 的假设 @
rtp_llm/utils/torch_patch.py:37- 建议:把该假设显式化为可检测项:或在 shim 处同样包装
scatter/all_gather(同为零拷贝 uint8 reinterpret,代价极低),或在已安装 wheel 契约测试中加一条针对 UE8M0 传输路径的断言/冒烟,并把 fastsafetensors 版本纳入注释中的验证范围说明,使上游升级时该假设失效能被发现而非表现为多 rank 加载崩溃。
- 建议:把该假设显式化为可检测项:或在 shim 处同样包装
Checklist Findings (17 fail / 48 total)
General Principles Checklist
- [6.1] Architecture — 依赖方向:无循环依赖/跨层惊喜 → issue
跨模块导入下划线私有符号,通用反射工具放错层
_loader.py:25-33 从rtp_llm.utils.database导入三个下划线私有函数(_apply_fastsafetensors_env_compat、_callable_accepts_keyword、_normalize_fastsafetensors_stacked_moe_mode)。其中_callable_accepts_keyword是与 checkpoint database 无关的通用签名反射工具,放在database.py造成层次错位;_apply_fastsafetensors_env_compat会进程级改写FASTSAFETENSORS_CONFIG_JSON(database.py:29-43),跨模块调用时副作用不易发现。同时 loader.py:545-547 与 per_channel_fp8_quant_weight.py:811-817 直接调用MoeAtomicWeight的_raw_stacked_tensor_names/ `has_raw_stacked_te - [6.1] Architecture — 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全 → issue
UE8M0 broadcast shim 的适用性断言外移为对上游 AutoLoader 的假设
torch_patch.py:36-39 的注释断言「Scatter is not exercised by fastsafetensors --AutoLoaderroutes UE8M0 transfers throughpg.broadcast; only the broadcast wrapper is needed」,据此只 patch 了torch.distributed.broadcast(:68-77)。本 PR 把 loader 构造完全交给上游AutoLoader(database.py:465-470),该「只走 broadcast」的传输路径成了纯上游实现假设。版本闸门(:54-65)只校验 torch 主次版本,不校验 fastsafetensors 版本,也没有任何测试覆盖多 rank UE8M0 路径;若上游改用scatter/all_gather投递 UE8M0 权重 scale,多 rank 加载会以 NCCL「data type is not supported」失败,而不会命中该 shim。 - [6.1] Architecture — 分层边界:新概念在正确层级,不泄漏内部 → issue
跨模块导入下划线私有符号,通用反射工具放错层
_loader.py:25-33 从rtp_llm.utils.database导入三个下划线私有函数(_apply_fastsafetensors_env_compat、_callable_accepts_keyword、_normalize_fastsafetensors_stacked_moe_mode)。其中_callable_accepts_keyword是与 checkpoint database 无关的通用签名反射工具,放在database.py造成层次错位;_apply_fastsafetensors_env_compat会进程级改写FASTSAFETENSORS_CONFIG_JSON(database.py:29-43),跨模块调用时副作用不易发现。同时 loader.py:545-547 与 per_channel_fp8_quant_weight.py:811-817 直接调用MoeAtomicWeight的_raw_stacked_tensor_names/ `has_raw_stacked_te - [6.1] Architecture — 可观测性:日志/指标/超时可操作、非噪声 → issue
文档承诺的falls back to scratch标记不覆盖内存预检与 AUTO 前置条件回退
文档 :226-227 声明「A scratch fallback containsfalls back to scratch」,把该字符串定义为识别所有 scratch 回退的可 grep 统一标记,docs/release/breaking-changes.md:29-30有同义表述。但实现中只有能力/包/import 类降级带该标记(loader.py:512-516)。两条内存预检回退分别只输出degraded_reason=memory-preflight-failed(loader.py:284-289)与full-stacked-memory-preflight-failed(loader.py:309-314),均不含该标记;AUTO 前置条件不满足(非 safetensor、convert_device == "cpu"、张量重名,loader.py:271-292)时完全不打降级日志。运维按文档 grep 做告警会系统性漏掉显存驱动与前置条件驱动的 scratch 回退,而这恰是加载性能回退最常见的原因。 - [6.1] Architecture — 回滚路径:风险行为存在运维回滚手段 → issue
能力表未说明缺失 local_copyout_filter 档位的显存代价,预算也未补偿
文档 :222 能力表把缺失local_copyout_filter的后果仅写为full materialization, RTP consumer filtering,未说明这意味着每个 rank 都会在设备上物化全部 tensor(database.py:454-459 的 warning 印证),TP/EP 越大峰值显存代价越明显。同时_fastsafetensors_transient_budget_bytes(loader.py:446-451)只为 full-stacked 追加一个 shard,对该 consumer-filter 档位不做任何预算补偿,AUTO 可能以偏低的transient_mem放行;:217 的package age alone does not fail model startup也未提示这一显存代价。 - [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue
AutoLoader 能力探测重复两份,database 侧降级分支与已固定的 copy-out 键集合不自洽
能力探测有两处实现:loader.py:472 的_fastsafetensors_capability_error,以及 database.py:419-430fastsafetensors_weights_iterator内的同名dim0_split_templates检查。后者在缺该能力时自行把 per-expert 降级为 full-stacked,但此刻required_checkpoint_keys(loader.py:645-647)已按 per-expert 构造(不含 raw stacked 键),并已作为local_copyout_filter传给 AutoLoader(database.py:452-453),上游会把整块 stacked 张量过滤掉;loader.py:638-643 的 full-stacked 高显存告警也不会打出。当前两处判据完全相同故该分支不可达,但任一侧改动即产生静默错误加载。 - [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue
文档称内存预检同样约束显式 fastsafetensors,实际默认 per-expert 完全跳过预检
文档 :214-217 把「an insufficient memory preflight falls back to scratch」与包缺失、import/ABI 失败并列,紧接着声明「These compatibility paths apply to bothautoand an explicitLOAD_METHOD=fastsafetensors」,:250 又让运维「Inspect thefastsafetensor memory checklog and itsenoughfield」。但 loader.py:305-308 中显式路径的预检被stacked_moe_mode == FULL_STACKED短路:生效模式为默认 per-expert 时_is_memory_enough_for_fastsafetensor根本不会被调用(直接走 :316),既不回退 scratch,也不会打印该预检日志。显存紧张节点上按文档预期存在兜底,实际会直接进入加载并可能 OOM 且无预检日志可查。 - [6.1] Software Engineering — DRY:重复非平凡逻辑被抽取或显式复用 → issue
tier 分类自行实现且与生产判据分叉,也不校验 AutoLoader 真实调用形状
:428-434 用inspect.signature(auto_loader.__init__).parameters加not in parameters自行分档;生产走_callable_accepts_keyword(database.py:74-89),额外要求参数 kind 属于POSITIONAL_OR_KEYWORD/KEYWORD_ONLY。对 positional-only 签名两者结论相反:测试报per-expert,生产降级为full-stacked。该用例也从不构造 AutoLoader、不校验iterate_weights/close,而生产调用形状是AutoLoader(pg, hf_weights_files, device=device, **kwargs)并在finally中close()(database.py:465-476)。若上游改名/重排前两个位置参数或改名close(),tier 仍判 per-expert,而真实加载抛TypeError/ ` - [6.1] Software Engineering — KISS/YAGNI:无投机性抽象 → issue ``_build_stacked_key_config
的database=None` 默认分支语义相反且存在无效计算`
_唯一生产调用方(loader.py:629-631)总是显式传入 `self._load_config.database`;`database=None` 分支只有既有测试(test_inline_fp8_quant.py:131、test_stacked_moe_weight.py:252/279/297 均为单参调用)与潜在误用会触达。两条分支语义相反:传库时跳过 ckpt 中不存在 raw stacked 张量的权重,不传库时对同一批权重全部登记,从而生成会被上游当作 `dim0_split_templates` 使用的错误键。此外 :545 的 `_raw_stacked_tensor_names` 在 :546-549 的跳过判断之前先行计算,而 `has_raw_stacked_tensors` 内部又重算一次,属无效开销。 - [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue
UE8M0 broadcast shim 的适用性断言外移为对上游 AutoLoader 的假设
torch_patch.py:36-39 的注释断言「Scatter is not exercised by fastsafetensors --AutoLoaderroutes UE8M0 transfers throughpg.broadcast; only the broadcast wrapper is needed」,据此只 patch 了torch.distributed.broadcast(:68-77)。本 PR 把 loader 构造完全交给上游AutoLoader(database.py:465-470),该「只走 broadcast」的传输路径成了纯上游实现假设。版本闸门(:54-65)只校验 torch 主次版本,不校验 fastsafetensors 版本,也没有任何测试覆盖多 rank UE8M0 路径;若上游改用scatter/all_gather投递 UE8M0 权重 scale,多 rank 加载会以 NCCL「data type is not supported」失败,而不会命中该 shim。 - [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue
假 fastsafetensors 模块直接改写全局 sys.modules,隔离方式脆弱
_install_fake_fastsafetensors是模块级 helper,内部直接执行sys.modules["fastsafetensors"] = module(:33)且自身不做还原,完全依赖FastsafetensorsAutoLoaderTest.setUp(:107-110)的patch.dict(sys.modules, {}, clear=False)兜底。该还原方式退出时会先清空整个sys.modules再灌回快照,测试体内新导入的模块会被逐出并重跑模块级副作用。任何未带同等 patch 的新测试类若调用该 helper,假模块会泄漏到InstalledFastsafetensorsContractTest.setUp(:409-411),使真实 wheel 契约检查以断言失败收场,掩盖真正问题。 - [6.1] Tests — 被删除测试有等价替代覆盖 → issue
已安装 wheel 契约测试在其 target 中必然 skip,被删除的版本门禁无等价替代
_test_available_auto_loader_contract_is_classified仅在RTP_LLM_EXPECT_FASTSAFETENSORS_TIER非空时把低档位判为失败,否则skipTest(:419-422、:441-442)。该用例只存在于//rtp_llm/utils/test:ckpt_database_test,其 deps 为 utils/config/ft_pickler/lora(utils/test/BUILD:95-100),而//rtp_llm:utils(rtp_llm/BUILD:159-177)不含 fastsafetensors requirement,故import fastsafetensors(:416)在沙箱内必然 ImportError。全仓检索确认该环境变量仅出现在文档 :230 与用例 :414,无任何 target 或 CI 脚本设置它;同时_REQUIRED_FST_VERSION硬版本门已随本 PR 删除(零残留),wheel 能力漂移在 CI 中因此完全无拦截。 - [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue ``_has_raw_stacked_tensors
的多 ckpt weight「全存在」语义缺少边界覆盖
_ffn_weight.py:379-383 的语义是 `bool(names) and all(tensor_source.has_tensor(name) for name in names)`,即同一 atomic weight 的全部 raw 键都必须存在才走逻辑 per-expert 键,该判定同时影响 `build_stacked_key_config`(loader.py:546-549)。但新增用例只覆盖单个 ckpt weight(:477-496,断言 `has_tensor` 恰好调用一次)与不同 atomic weight 之间的混合情形(:498-526);「同一 atomic weight 含 w_gate/w_up 两个 ckpt weight、其中一个 raw 张量缺失」这一 `all()` 关键边界没有任何用例。
RTP-LLM Checklist
- [I] 代码质量 — 同一功能用统一工具函数 → issue
跨模块导入下划线私有符号,通用反射工具放错层
_loader.py:25-33 从rtp_llm.utils.database导入三个下划线私有函数(_apply_fastsafetensors_env_compat、_callable_accepts_keyword、_normalize_fastsafetensors_stacked_moe_mode)。其中_callable_accepts_keyword是与 checkpoint database 无关的通用签名反射工具,放在database.py造成层次错位;_apply_fastsafetensors_env_compat会进程级改写FASTSAFETENSORS_CONFIG_JSON(database.py:29-43),跨模块调用时副作用不易发现。同时 loader.py:545-547 与 per_channel_fp8_quant_weight.py:811-817 直接调用MoeAtomicWeight的_raw_stacked_tensor_names/ `has_raw_stacked_te
Python Static-First Checklist
- [P.F] 语言陷阱 — 禁止模块级 import 副作用 → issue
假 fastsafetensors 模块直接改写全局 sys.modules,隔离方式脆弱
_install_fake_fastsafetensors是模块级 helper,内部直接执行sys.modules["fastsafetensors"] = module(:33)且自身不做还原,完全依赖FastsafetensorsAutoLoaderTest.setUp(:107-110)的patch.dict(sys.modules, {}, clear=False)兜底。该还原方式退出时会先清空整个sys.modules再灌回快照,测试体内新导入的模块会被逐出并重跑模块级副作用。任何未带同等 patch 的新测试类若调用该 helper,假模块会泄漏到InstalledFastsafetensorsContractTest.setUp(:409-411),使真实 wheel 契约检查以断言失败收场,掩盖真正问题。 - [P.G] 测试规范 — mock.patch target 是使用处而非定义处 → issue
假 fastsafetensors 模块直接改写全局 sys.modules,隔离方式脆弱
_install_fake_fastsafetensors是模块级 helper,内部直接执行sys.modules["fastsafetensors"] = module(:33)且自身不做还原,完全依赖FastsafetensorsAutoLoaderTest.setUp(:107-110)的patch.dict(sys.modules, {}, clear=False)兜底。该还原方式退出时会先清空整个sys.modules再灌回快照,测试体内新导入的模块会被逐出并重跑模块级副作用。任何未带同等 patch 的新测试类若调用该 helper,假模块会泄漏到InstalledFastsafetensorsContractTest.setUp(:409-411),使真实 wheel 契约检查以断言失败收场,掩盖真正问题。 - [P.G] 测试规范 — mock/fake/stub 不得替代本次声称覆盖的生产边界 → issue
tier 分类自行实现且与生产判据分叉,也不校验 AutoLoader 真实调用形状
:428-434 用inspect.signature(auto_loader.__init__).parameters加not in parameters自行分档;生产走_callable_accepts_keyword(database.py:74-89),额外要求参数 kind 属于POSITIONAL_OR_KEYWORD/KEYWORD_ONLY。对 positional-only 签名两者结论相反:测试报per-expert,生产降级为full-stacked。该用例也从不构造 AutoLoader、不校验iterate_weights/close,而生产调用形状是AutoLoader(pg, hf_weights_files, device=device, **kwargs)并在finally中close()(database.py:465-476)。若上游改名/重排前两个位置参数或改名close(),tier 仍判 per-expert,而真实加载抛TypeError/ `
Strengths
_raw_stacked_tensor_names(ffn_weight.py:364-377)显式排除含{expert_id}的模板,修掉了旧代码对 per-expert 布局调用tensor_name()会抛KeyError(或把 expert 0 误判为 stacked 张量)的真实缺陷,并把此前四处内联的self.weights[0].tensor_name(layer_id)收敛为单一判据,注释把动机写清楚了;test_per_expert_template_never_probes_raw_stacked_key锁定该修复,上一轮 P1 确认已解决。- 删除了依赖上游约 9 个私有内部结构(
fb._get_rank_lidx、fb.instantiated、factory.free_dev_ptrs等)与硬编码_REQUIRED_FST_VERSION="0.1.19"的PerExpertParallelLoader。全仓检索确认PerExpertParallelLoader/per_expert_parallel_loader/_REQUIRED_FST_VERSION零残留(BUILD 用glob(["*.py"])自动排除),并提供替代实现、迁移说明与双回滚路径,满足 R.I.2。 - 把版本号前缀硬失败改为
inspect.signature能力探测后,旧 wheel 表现为按档位降级而非启动失败;三级降级与requested_mode/effective_mode/degraded_reason结构化字段设计得当,「包未安装记 INFO、其余降级记 WARNING」的分级避免了噪声告警。 _fastsafetensors_transient_budget_bytes(loader.py:388-458)对上游estimated_peak_device_bytes做了完整脏值防御(None、bool特判、非数值、非有限、非正),test_invalid_estimates_use_three_max_files用subTest覆盖None/0/-1/"8192"/inf/True六类边界,把isinstance(True, int)这一经典陷阱显式钉住。_callable_accepts_keyword(database.py:74-89)明确拒绝把**kwargs当成能力声明,避免兼容层静默丢弃优化关键字后被误判为高阶能力,并有专门用例锁定;ckpt_database_test.py:250-257用untyped_storage().data_ptr()断言 full-stacked 路径真的clone而非返回视图,精准守护了「loader 可能在批次推进后释放 batch buffer」这一关键不变量。- 文档把
RTP_FASTSAFETENSORS_STACKED_MOE_MODE明确定位为过渡开关(env-only、不进--help、不进 config dump、非法值抛ValueError),并如实写出FASTSAFETENSORS_NOGDS=1会进程级覆写FASTSAFETENSORS_CONFIG_JSON且对同进程后续 loader 持续生效(server_arguments.md:252-260 与 database.py:29-43 一致);对上游不由 RTP 保证的 precedence 诚实标注为 upstream contract,未过度承诺。 - 新增策略用例通过
env = {"CUDA_VISIBLE_DEVICES": ""}保持 CPU-only(model_loader/test/BUILD:5-10),把选路策略从 GPU 独占目标中剥离为可确定性运行的用例,与同目录既有约定一致。
| where RTP expands it after AutoLoader yields the complete tensor. | ||
| """ | ||
|
|
||
| keys = set(tensor_to_weight_map.keys()) |
There was a problem hiding this comment.
[P2] per-expert 模式把 raw stacked 键排除出 copy-out 白名单,依赖未经真 wheel 验证的上游过滤时机
_build_fastsafetensors_local_copyout_keys 仅在 full-stacked 模式把 raw stacked 键并入白名单(:577-579),per-expert 刻意排除,test_fastsafetensors_loader_policy.py:30-40 还断言结果不含 "stacked.raw"。这隐含假定上游 AutoLoader 先按 dim0_split_templates 展开、再对展开后的逻辑名调用 local_copyout_filter(database.py:460-464 把展开完全交由上游)。若上游实际先在 raw checkpoint 键上过滤,整块 stacked MoE 张量会被丢弃,所有 MoE 权重静默落回 loader.py:726-750 的 database 兜底,优化完全失效且线索只有 :735 一条 INFO。该语义目前只被 patch.dict(sys.modules, ...) 的 fake module 覆盖,且过滤集合随 EP rank 变化,无多 rank 覆盖。
建议: 在该函数注释里把「上游先 split 后 filter」标注为显式契约假设,并把已安装 wheel 契约测试从「只校验签名」扩展到「同时装配 dim0_split_templates + local_copyout_filter 时 raw 键不会被提前丢弃、stacked 张量仍被切分投递」;再补一例 FakeAutoLoader 声明了 dim0_split_templates 但 iterate_weights 仍返回 raw 键的交叉场景,断言期望行为。保守替代方案是 per-expert 也并入 stacked_key_config.keys(),让过滤时机差异不再影响正确性。另建议给 loader.py:731 的兜底计数加 WARNING 阈值,让大面积静默回退可被告警发现。
| ) | ||
| budget = legacy_budget | ||
| else: | ||
| budget = int(estimate) |
There was a problem hiding this comment.
[P2] 取消 3-shard 下限后,上游预估失真会把优雅降级变成加载期 OOM
原判据固定为 (free_mem - model_mem) > 3 × max_shard。改动后只要上游 estimated_peak_device_bytes 为任意正数即整体替换该阈值,日志明确写 accepts positive upstream estimate without applying the legacy floor(:426-431)。若上游值只统计 loader 自有 buffer,未计入 RTP 侧 expert clone()(database.py:115)与 inline FP8 临时张量,AUTO 会把原本回退 scratch 的部署放行,失败形态从「自动降级」退化为加载期 CUDA OOM。per-expert 无任何下限保护,只有 full-stacked 才追加一份 max_file_size(:446-451)。文档 server_arguments.md:246-250 只描述了收紧方向,未说明该放松方向。
建议: 为 per-expert 也保留一个可关闭的保守下限(例如 max(upstream_estimate, max_file_size))或可配安全系数;至少在采纳值明显小于单个 shard 时打 WARNING,并同时打印上游原值与最终采用值,把「上游可能低估」的风险前置到启动日志。同步在文档该段补一句双向说明:上游估计小于历史 3 × max checkpoint shard 时 AUTO 准入会比旧版更宽松,可通过 fastsafetensor memory check 的 transient_mem 字段核对实际生效预算。
| "or use LOAD_METHOD=scratch for the conservative rollback" | ||
| ) | ||
|
|
||
| required_checkpoint_keys = self._build_fastsafetensors_local_copyout_keys( |
There was a problem hiding this comment.
[P2] AutoLoader 能力探测重复两份,database 侧降级分支与已固定的 copy-out 键集合不自洽
能力探测有两处实现:loader.py:472 的 _fastsafetensors_capability_error,以及 database.py:419-430 fastsafetensors_weights_iterator 内的同名 dim0_split_templates 检查。后者在缺该能力时自行把 per-expert 降级为 full-stacked,但此刻 required_checkpoint_keys(loader.py:645-647)已按 per-expert 构造(不含 raw stacked 键),并已作为 local_copyout_filter 传给 AutoLoader(database.py:452-453),上游会把整块 stacked 张量过滤掉;loader.py:638-643 的 full-stacked 高显存告警也不会打出。当前两处判据完全相同故该分支不可达,但任一侧改动即产生静默错误加载。
建议: 保留单一决策点:由 ModelLoader._resolve_fastsafetensors_mode 决定最终 mode,fastsafetensors_weights_iterator 只消费传入的 stacked_moe_mode;若必须保留 database 侧防御,则应在降级时直接 raise(或回传实际 mode 供上层重建键集合并重打告警),而不是在键集合已固定的情况下静默换路。
Checklist: [6.1] 状态不变量:创建/更新/失败/重试/回滚路径有效
|
|
||
| @staticmethod | ||
| def _build_stacked_key_config(weight_info_list) -> dict: | ||
| def _build_stacked_key_config(weight_info_list, database=None) -> dict: |
There was a problem hiding this comment.
[P2] _build_stacked_key_config 的 database=None 默认分支语义相反且存在无效计算
唯一生产调用方(loader.py:629-631)总是显式传入 self._load_config.database;database=None 分支只有既有测试(test_inline_fp8_quant.py:131、test_stacked_moe_weight.py:252/279/297 均为单参调用)与潜在误用会触达。两条分支语义相反:传库时跳过 ckpt 中不存在 raw stacked 张量的权重,不传库时对同一批权重全部登记,从而生成会被上游当作 dim0_split_templates 使用的错误键。此外 :545 的 _raw_stacked_tensor_names 在 :546-549 的跳过判断之前先行计算,而 _has_raw_stacked_tensors 内部又重算一次,属无效开销。
建议: 把 database 改为必填(或直接读 self._load_config.database 并去掉静态方法),删除易误用的 None 兜底并同步更新四处单参测试调用;把 _raw_stacked_tensor_names 的调用移到跳过判断之后并与 _has_raw_stacked_tensors 合并计算,使「是否登记 stacked 键」与 get_tensor_names / _load_raw_tensor 共享唯一判据来源。
Checklist: [6.1] KISS/YAGNI:无投机性抽象
| frozenset({"direct.weight", "expanded.experts.0", "stacked.raw"}), | ||
| ) | ||
|
|
||
| def test_rank_local_copyout_filter_and_mode_are_forwarded(self): |
There was a problem hiding this comment.
[P2] 新增策略测试全量 mock,两个 P0 恰落在被 mock 的加载边界上
test_rank_local_copyout_filter_and_mode_are_forwarded 用 object.__new__(ModelLoader) 绕过构造,把 _build_stacked_key_config、_create_model_weights、_generate_weight_info 与 weight.load 全部替换为 MagicMock(:55-90),只能验证参数透传。新增的 636 行用例中没有任何一个用真实 MoeAtomicWeight + 真实 TensorCollector 走通一次完整加载,而本次两个 P0 恰好落在 weight.load → _load_raw_tensor 这条被 mock 掉的边界上。copy-out 键与逻辑键的闭包也只由独立字面量断言分别维护(:31 用合成键 {"stacked.raw": ...},:492/:525 各自钉住真实键与模板),没有一条用例把「真实 MoE weight → stacked_key_config → copy-out 集...
建议: 补一个 CPU-only 端到端用例:构造真实 MoeAtomicWeight(stacked_ckpt_keys=True),用 get_tensor_names 产出的键建 TensorCollector,逐个逻辑 expert 键 store_tensor 后调用 weight.load(collector, ...),断言输出 shape 与 DatabaseTensorSource 路径一致——该用例可直接把两个 P0 钉死,成本很低。再补一个闭包不变量用例:把 _build_stacked_key_config 的模板按 range(expert_num) 展开,断言覆盖全部被选 expert 且是 get_tensor_names() 的子集。
| `RTP_FASTSAFETENSORS_STACKED_MOE_MODE` is a transitional, environment-only | ||
| switch: it has no command-line flag, is not shown by `--help`, and is not part | ||
| of the startup config dump. It is read only when the FastSafeTensors path is | ||
| considered. Values are case-sensitive and use a hyphen; any non-empty value |
There was a problem hiding this comment.
[P3] 取值边界与 per-expert 投递机制的文档表述不够精确
两处偏差。其一,:288-290 称「any non-empty value other than per-expert or full-stacked raises ValueError」,但 _normalize_fastsafetensors_stacked_moe_mode(database.py:59-63)先 strip(),纯空白值(如 " ")虽属 non-empty 却回落默认值而不抛错。其二,:262-264 把「source rank 先切片、各 rank 逐 expert broadcast」写成 RTP 的行为,但本 PR 已删除自实现的 per-expert loader,现在仅通过 database.py:460-464 的 dim0_split_templates 委托上游 AutoLoader,切片与 broadcast 时序不再由 RTP 决定。
建议: 把 :288-290 改为「去除首尾空白后为空则取默认值;其余不等于 per-expert / full-stacked 的取值抛 ValueError」。把 :262-264 改为描述意图与委托关系,例如「默认通过 dim0_split_templates 请求上游 AutoLoader 以有界显存逐 expert 投递,具体切片与 broadcast 时序由所安装的 fastsafetensors 版本决定」,避免把上游实现细节固化为 RTP 的对外承诺而随上游升级静默失真。
|
|
||
| | Capability | Present | Missing | | ||
| |---|---|---| | ||
| | `local_copyout_filter` | rank-local copy-out | full materialization, RTP consumer filtering | |
There was a problem hiding this comment.
[P3] 能力表未说明缺失 local_copyout_filter 档位的显存代价,预算也未补偿
文档 :222 能力表把缺失 local_copyout_filter 的后果仅写为 full materialization, RTP consumer filtering,未说明这意味着每个 rank 都会在设备上物化全部 tensor(database.py:454-459 的 warning 印证),TP/EP 越大峰值显存代价越明显。同时 _fastsafetensors_transient_budget_bytes(loader.py:446-451)只为 full-stacked 追加一个 shard,对该 consumer-filter 档位不做任何预算补偿,AUTO 可能以偏低的 transient_mem 放行;:217 的 package age alone does not fail model startup 也未提示这一显存代价。
建议: 在能力表下方补一句:缺失 local_copyout_filter 时所有 rank 会物化全部 tensor,峰值显存高于 rank-local copy-out 档位,且当前内存预检不为该档位追加预算;显存紧张的部署应优先升级 wheel 或改用 LOAD_METHOD=scratch。
Checklist: [6.1] 回滚路径:风险行为存在运维回滚手段
| ) | ||
| database.has_tensor.assert_not_called() | ||
|
|
||
| def test_existing_raw_stacked_tensor_uses_logical_expert_keys(self): |
There was a problem hiding this comment.
[P3] _has_raw_stacked_tensors 的多 ckpt weight「全存在」语义缺少边界覆盖
ffn_weight.py:379-383 的语义是 bool(names) and all(tensor_source.has_tensor(name) for name in names),即同一 atomic weight 的全部 raw 键都必须存在才走逻辑 per-expert 键,该判定同时影响 _build_stacked_key_config(loader.py:546-549)。但新增用例只覆盖单个 ckpt weight(:477-496,断言 has_tensor 恰好调用一次)与不同 atomic weight 之间的混合情形(:498-526);「同一 atomic weight 含 w_gate/w_up 两个 ckpt weight、其中一个 raw 张量缺失」这一 all() 关键边界没有任何用例。
建议: 补一个 weights=[CkptWeightInfo("...w_gate"), CkptWeightInfo("...w_up")] 且 has_tensor 仅对其中之一返回 True 的用例,断言 get_tensor_names 回退到原始 ckpt 名而非逻辑 per-expert 名,并断言该 atomic weight 不会进入 _build_stacked_key_config 结果,把 all() 语义钉死。
Checklist: [6.1] 边界 case 覆盖(空、单元素、最大值)
| # RTP-LLM's ``PerExpertParallelLoader`` route every UE8M0 transfer | ||
| # through ``pg.broadcast`` (dim=-1 in fastsafetensors / per-expert | ||
| # broadcast in PerExpertParallelLoader); only the broadcast wrapper is | ||
| # not exercised by fastsafetensors -- ``AutoLoader`` routes UE8M0 |
There was a problem hiding this comment.
[P3] UE8M0 broadcast shim 的适用性断言外移为对上游 AutoLoader 的假设
torch_patch.py:36-39 的注释断言「Scatter is not exercised by fastsafetensors -- AutoLoader routes UE8M0 transfers through pg.broadcast; only the broadcast wrapper is needed」,据此只 patch 了 torch.distributed.broadcast(:68-77)。本 PR 把 loader 构造完全交给上游 AutoLoader(database.py:465-470),该「只走 broadcast」的传输路径成了纯上游实现假设。版本闸门(:54-65)只校验 torch 主次版本,不校验 fastsafetensors 版本,也没有任何测试覆盖多 rank UE8M0 路径;若上游改用 scatter / all_gather 投递 UE8M0 权重 scale,多 rank 加载会以 NCCL「data type is not supported」失败,而不会命中该 shim。
建议: 把该假设显式化为可检测项:或在 shim 处同样包装 scatter / all_gather(同为零拷贝 uint8 reinterpret,代价极低),或在已安装 wheel 契约测试中加一条针对 UE8M0 传输路径的断言/冒烟,并把 fastsafetensors 版本纳入注释中的验证范围说明,使上游升级时该假设失效能被发现而非表现为多 rank 加载崩溃。
Checklist: [6.1] 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全;[6.1] 分布式/跨平台变更有对应覆盖
4e91157 to
66ef2a5
Compare
| self.assertFalse(getattr(installed_module, "_rtp_test_fake", False)) | ||
|
|
||
| def test_available_auto_loader_contract_is_classified(self) -> None: | ||
| expected_tier = os.environ.get("RTP_LLM_EXPECT_FASTSAFETENSORS_TIER") |
There was a problem hiding this comment.
[P2] 真实 wheel 能力契约在默认测试链路中可被跳过
未设置 RTP_LLM_EXPECT_FASTSAFETENSORS_TIER 时,缺包或低 tier 会跳过;仓库也未设置该变量。测试仅复刻签名分类并检查估算字段,未用真实 wheel 执行过滤、dim0 拆分、迭代及 tensor 生命周期契约。
建议: 增加依赖真实 wheel 且设置最低 tier 的必跑目标,用最小 stacked checkpoint 验证过滤、拆分、数值、所有权和多 rank 路径。
Checklist: [6.1] 分布式/跨平台变更有对应覆盖;[P.G] mock/fake/stub 不得替代本次声称覆盖的生产边界
| for resolved, level, expected in cases: | ||
| with self.subTest(resolved=resolved): | ||
| loader._resolve_fastsafetensors_mode = MagicMock(return_value=resolved) | ||
| with self.assertLogs(level=level) as logs: |
There was a problem hiding this comment.
[P2] 日志级别契约未被真正断言,assertLogs 的 level 只是捕获阈值
assertLogs(level=level) 只设置最低捕获等级,随后仅检查消息文本。预期 INFO 的记录变成 WARNING/ERROR,或预期 WARNING 的记录变成 ERROR 时,测试仍会通过。
建议: 显式断言 logs.records 的 levelno,并验证各场景不存在意外的更高等级记录。
Checklist: [6.1] 可观测性:日志/指标/超时可操作、非噪声;[6.1] 新逻辑有聚焦单测 + 相关集成/smoke 测试
0f7c377 to
219491b
Compare
LLLLKKKK
left a comment
There was a problem hiding this comment.
AI Code Review - PR #1354
Status: BLOCKING
Summary: P0/0 · P1/2 · P2/6 · P3/0
Reviewed: commit 219491b644ff · 2026-09-01 09:05 UTC+8
Blocking Issues
P1
- 显存预检丢弃 legacy floor 且未计入 RTP 侧 TensorCollector 常驻显存,默认 AUTO 可能误判可用并在加载期 OOM @
rtp_llm/model_loader/loader.py:425- 建议:至少取上游估值与集成侧安全下限的最大值,或显式计入 collector 与最终权重同时存活的峰值,并增加跨 batch 收集大权重的阈值测试。
- AutoLoader 运行期不兼容不会回退 scratch @
rtp_llm/utils/database.py:417- 建议:完整探测基础接口,并在首个模型张量写入前捕获已知导入、构造及 ABI 异常;协调各 rank 关闭资源后回退
scratch,并补充真实构造和首次迭代测试。
- 建议:完整探测基础接口,并在首个模型张量写入前捕获已知导入、构造及 ABI 异常;协调各 rank 关闭资源后回退
Non-blocking Suggestions
P2
- 无 stacked 权重仍会增加 full-stacked 显存预算 @
rtp_llm/model_loader/loader.py:446- 建议:先识别 checkpoint 是否存在原始堆叠键,仅在确实需要 dim0 split 时降级并增加预算;补充 dense 和原生 per-expert 用例。
- 文档称内存预检同样约束显式 fastsafetensors,实际默认 per-expert 完全跳过预检 @
docs/backend/server_arguments.md:216- 建议:分别说明各模式的预检语义,或为显式
per-expert补齐预检、回退和测试。
- 建议:分别说明各模式的预检语义,或为显式
- 真实 wheel 能力契约在默认测试链路中可被跳过 @
rtp_llm/utils/test/ckpt_database_test.py:414- 建议:增加依赖真实 wheel、按平台固定预期 tier 的测试目标,并使用小型 checkpoint 验证构造、拆分、过滤和关闭行为。
- 文档承诺的 falls back to scratch 标记不覆盖两处内存预检回退,consumer-filter 档位也无结构化字段 @
docs/backend/server_arguments.md:226- 建议:通过统一日志辅助函数输出所有降级与回退事件,或收窄文档中的结构化日志承诺,并补充完整日志断言。
- 日志级别契约未被真正断言,assertLogs 的 level 只是捕获阈值 @
rtp_llm/model_loader/test/test_fastsafetensors_loader_policy.py:443- 建议:断言
logs.records中目标记录的levelname,并隔离无关日志。
- 建议:断言
- Inline FP8 测试未验证专家数据和 scale 的对应关系 @
rtp_llm/model_loader/test/test_fastsafetensors_loader_policy.py:575- 建议:保存逐 expert 的预期 FP8 tensor 与 scale,并分别校验输出值、顺序和对应关系。
Checklist Findings (9 fail / 104 total)
General Principles Checklist
- [6.1] Architecture — 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全 → issue
文档承诺的 falls back to scratch 标记不覆盖两处内存预检回退,consumer-filter 档位也无结构化字段
AUTO 与显式 full-stacked 的内存回退日志均缺少文档承诺的falls back to scratch;缺少local_copyout_filter时的警告也没有requested_mode、effective_mode、degraded_reason字段。 - [6.1] Architecture — 可观测性:日志/指标/超时可操作、非噪声 → issue
日志级别契约未被真正断言,assertLogs 的 level 只是捕获阈值
assertLogs(level=level)只设置最低捕获级别,随后测试仅检查消息文本;预期 INFO 的日志改成 WARNING 仍会通过,无法保护文档声明的日志级别契约。 - [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue
无 stacked 权重仍会增加 full-stacked 显存预算
模式解析发生在stacked_key_config构建之前,并无条件检查dim0_split_templates。旧 wheel 会令 dense 或原生 per-expert checkpoint 降级为full-stacked,即使没有原始堆叠键也额外计入一个最大 shard,可能不必要地回退scratch。 - [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue
文档承诺的 falls back to scratch 标记不覆盖两处内存预检回退,consumer-filter 档位也无结构化字段
AUTO 与显式 full-stacked 的内存回退日志均缺少文档承诺的falls back to scratch;缺少local_copyout_filter时的警告也没有requested_mode、effective_mode、degraded_reason字段。 - [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue
真实 wheel 能力契约在默认测试链路中可被跳过
默认 BUILD 目标未设置RTP_LLM_EXPECT_FASTSAFETENSORS_TIER;缺包、能力层级不足或缺少load_config时测试均可跳过。其余用例使用伪造 AutoLoader,因此 CI 可能未验证真实 wheel 的构造和迭代契约。 - [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue
Inline FP8 测试未验证专家数据和 scale 的对应关系
测试为两个 expert 生成不同 tensor 和 scale,最终却只断言输出形状;重复 expert 0、交换 expert 顺序或关联错误 scale 均不会失败,未覆盖 W2 预量化路径的数值契约。
RTP-LLM Checklist
- [H] 测试与 CI — mock/fake/stub 只能隔离非目标依赖,不能 stub 掉本次声称覆盖的生产边界;涉及 pybind/C++/runtime 的 bug 必须有真实边界集成或 smoke 覆盖 → issue
真实 wheel 能力契约在默认测试链路中可被跳过
默认 BUILD 目标未设置RTP_LLM_EXPECT_FASTSAFETENSORS_TIER;缺包、能力层级不足或缺少load_config时测试均可跳过。其余用例使用伪造 AutoLoader,因此 CI 可能未验证真实 wheel 的构造和迭代契约。 - [H] 测试与 CI — 新增、迁移或删除测试必须证明目标行为仍在 CI 中执行;DISABLED_、#if 0、open_skip、导入即失败、未被 target 引用的用例不得计为覆盖 → issue
真实 wheel 能力契约在默认测试链路中可被跳过
默认 BUILD 目标未设置RTP_LLM_EXPECT_FASTSAFETENSORS_TIER;缺包、能力层级不足或缺少load_config时测试均可跳过。其余用例使用伪造 AutoLoader,因此 CI 可能未验证真实 wheel 的构造和迭代契约。
Python Static-First Checklist
- [P.G] 测试规范 — mock/fake/stub 不得替代本次声称覆盖的生产边界 → issue
真实 wheel 能力契约在默认测试链路中可被跳过
默认 BUILD 目标未设置RTP_LLM_EXPECT_FASTSAFETENSORS_TIER;缺包、能力层级不足或缺少load_config时测试均可跳过。其余用例使用伪造 AutoLoader,因此 CI 可能未验证真实 wheel 的构造和迭代契约。
Strengths
- 正确区分原始堆叠键与逻辑 expert 键。
- 移除了对上游私有加载器实现的依赖。
- 新增模式解析、内存预算、键过滤、FP8 与资源关闭测试。
- rank-local copy-out 使用不可变键集合,边界清晰。
| ) | ||
| budget = legacy_budget | ||
| else: | ||
| budget = int(estimate) |
There was a problem hiding this comment.
[P1] 显存预检丢弃 legacy floor 且未计入 RTP 侧 TensorCollector 常驻显存,默认 AUTO 可能误判可用并在加载期 OOM
任意正数 estimated_peak_device_bytes 都会替代 3 * max_file_size,但该值只覆盖 loader-owned buffers。TensorCollector 会跨迭代保留输入,weight.load() 又在 clear() 前分配最终权重;较小上游估值可能使 AUTO 通过预检后在输入、输出重叠阶段 OOM。
建议: 至少取上游估值与集成侧安全下限的最大值,或显式计入 collector 与最终权重同时存活的峰值,并增加跨 batch 收集大权重的阈值测试。
| self.assertFalse(getattr(installed_module, "_rtp_test_fake", False)) | ||
|
|
||
| def test_available_auto_loader_contract_is_classified(self) -> None: | ||
| expected_tier = os.environ.get("RTP_LLM_EXPECT_FASTSAFETENSORS_TIER") |
There was a problem hiding this comment.
[P2] 真实 wheel 能力契约在默认测试链路中可被跳过
默认 BUILD 目标未设置 RTP_LLM_EXPECT_FASTSAFETENSORS_TIER;缺包、能力层级不足或缺少 load_config 时测试均可跳过。其余用例使用伪造 AutoLoader,因此 CI 可能未验证真实 wheel 的构造和迭代契约。
建议: 增加依赖真实 wheel、按平台固定预期 tier 的测试目标,并使用小型 checkpoint 验证构造、拆分、过滤和关闭行为。
Checklist: [6.1] 分布式/跨平台变更有对应覆盖;[P.G] mock/fake/stub 不得替代本次声称覆盖的生产边界;[H] mock/fake/stub 只能隔离非目标依赖,不能 stub 掉本次声称覆盖的生产边界;涉及 pybind/C++/runtime 的 bug 必须有真实边界集成或 smoke 覆盖;[H] 新增、迁移或删除测试必须证明目标行为仍在 CI 中执行;DISABLED_、#if 0、open_skip、导入即失败、未被 target 引用的用例不得计为覆盖
| for resolved, level, expected in cases: | ||
| with self.subTest(resolved=resolved): | ||
| loader._resolve_fastsafetensors_mode = MagicMock(return_value=resolved) | ||
| with self.assertLogs(level=level) as logs: |
There was a problem hiding this comment.
[P2] 日志级别契约未被真正断言,assertLogs 的 level 只是捕获阈值
assertLogs(level=level) 只设置最低捕获级别,随后测试仅检查消息文本;预期 INFO 的日志改成 WARNING 仍会通过,无法保护文档声明的日志级别契约。
建议: 断言 logs.records 中目标记录的 levelname,并隔离无关日志。
Checklist: [6.1] 可观测性:日志/指标/超时可操作、非噪声
| torch.testing.assert_close(collector_result, database_result) | ||
| torch.testing.assert_close(collector_result, raw_tensor) | ||
|
|
||
| def test_inline_fp8_uses_logical_keys_from_collector(self): |
There was a problem hiding this comment.
[P2] Inline FP8 测试未验证专家数据和 scale 的对应关系
测试为两个 expert 生成不同 tensor 和 scale,最终却只断言输出形状;重复 expert 0、交换 expert 顺序或关联错误 scale 均不会失败,未覆盖 W2 预量化路径的数值契约。
建议: 保存逐 expert 的预期 FP8 tensor 与 scale,并分别校验输出值、顺序和对应关系。
Checklist: [6.1] 新逻辑有聚焦单测 + 相关集成/smoke 测试
LLLLKKKK
left a comment
There was a problem hiding this comment.
AI Code Review - PR #1354
Status: BLOCKING
Summary: P0/0 · P1/2 · P2/7 · P3/0
Reviewed: commit f42155bc6059 · 2026-09-01 16:29 UTC+8
Blocking Issues
P1
- 显存预检丢弃 legacy floor 且未计入 RTP 侧 TensorCollector 常驻显存,默认 AUTO 可能误判可用并在加载期 OOM @
rtp_llm/model_loader/loader.py:425- 建议:预算应同时覆盖上游估算和 RTP collector、合并及量化的峰值下限,并用真实 wheel 与 stacked-MoE checkpoint 校准 AUTO 边界。
- AutoLoader 运行期不兼容不会回退 scratch @
rtp_llm/utils/database.py:417- 建议:预检全部必需符号和构造契约;对明确的兼容性异常清理未完成状态并从 scratch 重新加载,同时让 checkpoint 数据错误保持 fail-fast。
Non-blocking Suggestions
P2
- 无 stacked 权重仍会增加 full-stacked 显存预算 @
rtp_llm/model_loader/loader.py:446- 建议:仅在 checkpoint 实际包含 raw stacked MoE 权重时增加额外预算,并补充无 stacked 权重的准入边界测试。
- 文档称内存预检同样约束显式 fastsafetensors,实际默认 per-expert 完全跳过预检 @
docs/backend/server_arguments.md:216- 建议:明确显式 per-expert 尊重强制请求且不预检,或统一执行预检,并增加测试固定最终契约。
- 真实 wheel 能力契约在默认测试链路中可被跳过 @
rtp_llm/utils/test/ckpt_database_test.py:414- 建议:为各发布平台增加依赖真实 wheel 且期望
per-expert的契约目标,复用_callable_accepts_keyword,并实际验证 split/filter 顺序及仅 filter 档位。
- 建议:为各发布平台增加依赖真实 wheel 且期望
- 文档承诺的 falls back to scratch 标记不覆盖两处内存预检回退,consumer-filter 档位也无结构化字段 @
docs/backend/server_arguments.md:226- 建议:统一输出
requested_mode、effective_mode、degraded_reason和 scratch 标记,或准确缩小文档承诺范围。
- 建议:统一输出
- 日志级别契约未被真正断言,assertLogs 的 level 只是捕获阈值 @
rtp_llm/model_loader/test/test_fastsafetensors_loader_policy.py:443- 建议:逐场景断言捕获记录的
levelno或levelname,覆盖包缺失、能力降级、内存回退和正常选择。
- 建议:逐场景断言捕获记录的
- Inline FP8 测试未验证专家数据和 scale 的对应关系 @
rtp_llm/model_loader/test/test_fastsafetensors_loader_policy.py:575- 建议:逐专家比较 FP8 数据与 scale,或反量化后与源数据比较,并覆盖专家顺序及 gate/up 拼接关系。
- MoE 布局解析逻辑重复且跨类调用私有方法 @
rtp_llm/model_loader/per_channel_fp8_quant_weight.py:811- 建议:在
MoeAtomicWeight提供统一的公开布局解析结果,供普通 MoE 与 inline FP8 路径复用。
- 建议:在
Checklist Findings (12 fail / 121 total)
General Principles Checklist
- [6.1] Architecture — 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全 → issue
文档承诺的 falls back to scratch 标记不覆盖两处内存预检回退,consumer-filter 档位也无结构化字段
固定标记只出现在能力探测回退;AUTO 与显式 full-stacked 的内存回退均缺少该标记,AUTO 前置条件失败也无决策原因。缺少local_copyout_filter时仅输出普通 warning,没有文档承诺的结构化字段,按该契约配置的告警会漏报。 - [6.1] Architecture — 分层边界:新概念在正确层级,不泄漏内部 → issue
MoE 布局解析逻辑重复且跨类调用私有方法
普通 MoE 与 inline FP8 分别执行 database、logical key、raw stacked key 和 source 包装判定;量化类还直接调用MoeAtomicWeight的多个私有方法。布局规则变化需要同步两套实现,容易造成 weight 与 scale 路径漂移。 - [6.1] Architecture — 可观测性:日志/指标/超时可操作、非噪声 → issue
日志级别契约未被真正断言,assertLogs 的 level 只是捕获阈值
assertLogs(level=...)只设置最低捕获等级,后续仅断言文本。因此预期 INFO 实际输出 WARNING、预期 WARNING 实际输出 ERROR 时测试仍会通过,无法保护文档公开的日志级别契约。 - [6.1] Architecture — 回滚路径:风险行为存在运维回滚手段 → issue
无 stacked 权重仍会增加 full-stacked 显存预算
只要模式为full-stacked就无条件增加一个最大分片,而此时尚未构建stacked_key_config。dense、原生 per-expert 或没有 raw stacked key 的模型也可能因此被错误判定显存不足并回退 scratch。 - [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue
无 stacked 权重仍会增加 full-stacked 显存预算
只要模式为full-stacked就无条件增加一个最大分片,而此时尚未构建stacked_key_config。dense、原生 per-expert 或没有 raw stacked key 的模型也可能因此被错误判定显存不足并回退 scratch。 - [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue
文档承诺的 falls back to scratch 标记不覆盖两处内存预检回退,consumer-filter 档位也无结构化字段
固定标记只出现在能力探测回退;AUTO 与显式 full-stacked 的内存回退均缺少该标记,AUTO 前置条件失败也无决策原因。缺少local_copyout_filter时仅输出普通 warning,没有文档承诺的结构化字段,按该契约配置的告警会漏报。 - [6.1] Software Engineering — DRY:重复非平凡逻辑被抽取或显式复用 → issue
MoE 布局解析逻辑重复且跨类调用私有方法
普通 MoE 与 inline FP8 分别执行 database、logical key、raw stacked key 和 source 包装判定;量化类还直接调用MoeAtomicWeight的多个私有方法。布局规则变化需要同步两套实现,容易造成 weight 与 scale 路径漂移。 - [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue
真实 wheel 能力契约在默认测试链路中可被跳过
未设置RTP_LLM_EXPECT_FASTSAFETENSORS_TIER时,缺包或低能力会跳过,默认目标未设置该变量。测试还仅按参数名分类,没有复用生产端的关键字可调用判定,也没有通过真实 wheel 执行“先 split、后 local filter”的关键语义,制品能力回退仍可能假绿。 - [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue
Inline FP8 测试未验证专家数据和 scale 的对应关系
用例写入不同专家的 FP8 tensor 与 scale,最终却只断言输出 shape。专家顺序交换、重复专家或 weight/scale 错配均可保持形状不变并通过,而这些错误会直接改变推理结果。 - [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue
Inline FP8 测试未验证专家数据和 scale 的对应关系
用例写入不同专家的 FP8 tensor 与 scale,最终却只断言输出 shape。专家顺序交换、重复专家或 weight/scale 错配均可保持形状不变并通过,而这些错误会直接改变推理结果。
RTP-LLM Checklist
- [I] 代码质量 — 同一功能用统一工具函数 → issue
MoE 布局解析逻辑重复且跨类调用私有方法
普通 MoE 与 inline FP8 分别执行 database、logical key、raw stacked key 和 source 包装判定;量化类还直接调用MoeAtomicWeight的多个私有方法。布局规则变化需要同步两套实现,容易造成 weight 与 scale 路径漂移。
Python Static-First Checklist
- [P.G] 测试规范 — mock/fake/stub 不得替代本次声称覆盖的生产边界 → issue
Inline FP8 测试未验证专家数据和 scale 的对应关系
用例写入不同专家的 FP8 tensor 与 scale,最终却只断言输出 shape。专家顺序交换、重复专家或 weight/scale 错配均可保持形状不变并通过,而这些错误会直接改变推理结果。
Strengths
- 使用 AutoLoader 能力探测替代私有加载器 API。
- stacked-MoE 布局改由 checkpoint database 判定,避免模板 key 误分类。
- 多平台 requirements 与 lock 同步升级,并保留 scratch/full-stacked 回滚入口。
- 新增 CPU-only 策略测试,覆盖模式解析、预算边界和 key 闭包。
| ) | ||
| budget = legacy_budget | ||
| else: | ||
| budget = int(estimate) |
There was a problem hiding this comment.
[P1] 显存预检丢弃 legacy floor 且未计入 RTP 侧 TensorCollector 常驻显存,默认 AUTO 可能误判可用并在加载期 OOM
任意正数 estimated_peak_device_bytes 都会直接替代 3 × max_file_size。该估算只覆盖 loader 缓冲;TensorCollector 会保存组件的全部专家张量,随后 weight.load 分配最终输出,直到完成后才清理。AUTO 未为这段重叠占用预留空间,可能在紧显存部署中准入后 OOM。
建议: 预算应同时覆盖上游估算和 RTP collector、合并及量化的峰值下限,并用真实 wheel 与 stacked-MoE checkpoint 校准 AUTO 边界。
| self.assertFalse(getattr(installed_module, "_rtp_test_fake", False)) | ||
|
|
||
| def test_available_auto_loader_contract_is_classified(self) -> None: | ||
| expected_tier = os.environ.get("RTP_LLM_EXPECT_FASTSAFETENSORS_TIER") |
There was a problem hiding this comment.
[P2] 真实 wheel 能力契约在默认测试链路中可被跳过
未设置 RTP_LLM_EXPECT_FASTSAFETENSORS_TIER 时,缺包或低能力会跳过,默认目标未设置该变量。测试还仅按参数名分类,没有复用生产端的关键字可调用判定,也没有通过真实 wheel 执行“先 split、后 local filter”的关键语义,制品能力回退仍可能假绿。
建议: 为各发布平台增加依赖真实 wheel 且期望 per-expert 的契约目标,复用 _callable_accepts_keyword,并实际验证 split/filter 顺序及仅 filter 档位。
Checklist: [6.1] 分布式/跨平台变更有对应覆盖
| for resolved, level, expected in cases: | ||
| with self.subTest(resolved=resolved): | ||
| loader._resolve_fastsafetensors_mode = MagicMock(return_value=resolved) | ||
| with self.assertLogs(level=level) as logs: |
There was a problem hiding this comment.
[P2] 日志级别契约未被真正断言,assertLogs 的 level 只是捕获阈值
assertLogs(level=...) 只设置最低捕获等级,后续仅断言文本。因此预期 INFO 实际输出 WARNING、预期 WARNING 实际输出 ERROR 时测试仍会通过,无法保护文档公开的日志级别契约。
建议: 逐场景断言捕获记录的 levelno 或 levelname,覆盖包缺失、能力降级、内存回退和正常选择。
Checklist: [6.1] 可观测性:日志/指标/超时可操作、非噪声
| torch.testing.assert_close(collector_result, database_result) | ||
| torch.testing.assert_close(collector_result, raw_tensor) | ||
|
|
||
| def test_inline_fp8_uses_logical_keys_from_collector(self): |
There was a problem hiding this comment.
[P2] Inline FP8 测试未验证专家数据和 scale 的对应关系
用例写入不同专家的 FP8 tensor 与 scale,最终却只断言输出 shape。专家顺序交换、重复专家或 weight/scale 错配均可保持形状不变并通过,而这些错误会直接改变推理结果。
建议: 逐专家比较 FP8 数据与 scale,或反量化后与源数据比较,并覆盖专家顺序及 gate/up 拼接关系。
Checklist: [6.1] 新逻辑有聚焦单测 + 相关集成/smoke 测试;[6.1] 边界 case 覆盖(空、单元素、最大值);[P.G] mock/fake/stub 不得替代本次声称覆盖的生产边界
9780eff to
c41fdd2
Compare
LLLLKKKK
left a comment
There was a problem hiding this comment.
AI Code Review - PR #1354
Status: BLOCKING
Summary: P0/0 · P1/4 · P2/1 · P3/1
Reviewed: commit c41fdd23a8c3 · 2026-09-01 19:03 UTC+8
Blocking Issues
P1
- CUDA 13 ARM 依赖锁未接入架构选择器 @
arch_config/arch_select.bzl:25- 建议:加载 CUDA 13 ARM 仓库,并为
requirement()、torch_deps()、whl_deps()增加高优先级using_cuda13_arm分支及对应契约测试。
- 建议:加载 CUDA 13 ARM 仓库,并为
- 兼容回退时 traceback 会阻止已加载 GPU 权重释放 @
rtp_llm/model_loader/loader.py:334- 建议:在异常块内仅保存字符串化原因,退出异常作用域后再清理显存并执行 scratch;增加部分加载后抛出兼容异常的显存生命周期测试。
- wheel 契约测试未限制到支持的平台 @
rtp_llm/utils/test/BUILD:107- 建议:仅在提供新版 wheel 的配置启用该目标,或按平台拆分依赖和能力预期,并验证全部平台矩阵均可分析执行。
- 真实 FastSafeTensors 多 rank 加载边界未被回归覆盖 @
rtp_llm/utils/test/ckpt_database_test.py:507- 建议:在受支持 GPU 平台使用锁定 wheel 和小型 stacked checkpoint,覆盖单 rank、双 rank、rank-local expert 集合、输出数值、资源关闭及峰值显存。
Non-blocking Suggestions
P2
- full-stacked 开关会影响非 stacked checkpoint @
rtp_llm/model_loader/loader.py:321- 建议:仅在
_has_raw_stacked_moe_weights()为真时应用 full-stacked 预检;其他 checkpoint 保持显式 per-expert 路径的行为。
- 建议:仅在
P3
- scratch fallback 日志级别说明与实现不一致 @
docs/backend/server_arguments.md:232- 建议:明确包缺失和正常前置条件不满足均使用 INFO,或将实现调整为文档承诺的 WARNING。
Checklist Findings (13 fail / 121 total)
General Principles Checklist
- [6.1] Architecture — 依赖方向:无循环依赖/跨层惊喜 → issue
CUDA 13 ARM 依赖锁未接入架构选择器
pip_cuda13_arm_torch已注册,但选择器未加载该仓库,也没有using_cuda13_arm分支。该配置不匹配using_arm或 x86 分支,导致requirement()、torch_deps()落入 CPU 默认项,而whl_deps()匹配通用 CUDA 12 项;新增 ARM wheel 无法进入 Bazel 运行时。 - [6.1] Architecture — 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全 → issue
full-stacked 开关会影响非 stacked checkpoint
显式 FastSafeTensors 只要选择full-stacked就执行内存预检,尚未确认 checkpoint 是否包含 raw stacked 权重。密集或原生 per-expert checkpoint 也可能因该环境变量意外回退 scratch,违反该开关仅控制 stacked-MoE 交付方式的文档约定。 - [6.1] Architecture — 可观测性:日志/指标/超时可操作、非噪声 → issue
scratch fallback 日志级别说明与实现不一致
文档称除包缺失外的 fallback 均为 WARNING,但 AUTO 前置条件不满足时实现使用 INFO。依赖该文档配置 WARNING 监控会漏掉此类回退。 - [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue
full-stacked 开关会影响非 stacked checkpoint
显式 FastSafeTensors 只要选择full-stacked就执行内存预检,尚未确认 checkpoint 是否包含 raw stacked 权重。密集或原生 per-expert checkpoint 也可能因该环境变量意外回退 scratch,违反该开关仅控制 stacked-MoE 交付方式的文档约定。 - [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue
兼容回退时 traceback 会阻止已加载 GPU 权重释放
兼容异常可能在部分权重写入model_weights后抛出。当前在except ... as error作用域内清理并立即执行 scratch;活动异常及 traceback 仍引用_load_from_fastsafetensor帧和其中的 GPU tensor,清理无法释放这些显存,scratch 全量加载可能 OOM。 - [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue
真实 FastSafeTensors 多 rank 加载边界未被回归覆盖
删除私有PerExpertParallelLoader后,rank-local 过滤和 stacked-MoE 拆分交由上游AutoLoader。行为测试替换了整个包,installed-wheel 测试仅检查构造签名和配置字段,未实例化加载器或读取 checkpoint,无法验证真实双 rank 的拆分、过滤、collective 对齐和数值正确性。 - [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue
真实 FastSafeTensors 多 rank 加载边界未被回归覆盖
删除私有PerExpertParallelLoader后,rank-local 过滤和 stacked-MoE 拆分交由上游AutoLoader。行为测试替换了整个包,installed-wheel 测试仅检查构造签名和配置字段,未实例化加载器或读取 checkpoint,无法验证真实双 rank 的拆分、过滤、collective 对齐和数值正确性。 - [6.1] Tests — 被删除测试有等价替代覆盖 → issue
真实 FastSafeTensors 多 rank 加载边界未被回归覆盖
删除私有PerExpertParallelLoader后,rank-local 过滤和 stacked-MoE 拆分交由上游AutoLoader。行为测试替换了整个包,installed-wheel 测试仅检查构造签名和配置字段,未实例化加载器或读取 checkpoint,无法验证真实双 rank 的拆分、过滤、collective 对齐和数值正确性。
RTP-LLM Checklist
- [H] 测试与 CI — BUILD、py_test、cc_test、genrule 变更必须验证 srcs/data/runfiles/testdata 相对路径、import path 和 target 可执行性;genrule glob 不得捕获无关构建产物 → issue
wheel 契约测试未限制到支持的平台
该目标没有target_compatible_with或平台标签,却固定依赖//rtp_llm:fastsafetensors并要求per-expert。CPU、CUDA 12 和 ROCm 锁未提供该新版依赖,跨平台通配测试会在依赖分析或能力断言阶段失败。 - [H] 测试与 CI — mock/fake/stub 只能隔离非目标依赖,不能 stub 掉本次声称覆盖的生产边界;涉及 pybind/C++/runtime 的 bug 必须有真实边界集成或 smoke 覆盖 → issue
真实 FastSafeTensors 多 rank 加载边界未被回归覆盖
删除私有PerExpertParallelLoader后,rank-local 过滤和 stacked-MoE 拆分交由上游AutoLoader。行为测试替换了整个包,installed-wheel 测试仅检查构造签名和配置字段,未实例化加载器或读取 checkpoint,无法验证真实双 rank 的拆分、过滤、collective 对齐和数值正确性。 - [H] 测试与 CI — 新增、迁移或删除测试必须证明目标行为仍在 CI 中执行;DISABLED_、#if 0、open_skip、导入即失败、未被 target 引用的用例不得计为覆盖 → issue
真实 FastSafeTensors 多 rank 加载边界未被回归覆盖
删除私有PerExpertParallelLoader后,rank-local 过滤和 stacked-MoE 拆分交由上游AutoLoader。行为测试替换了整个包,installed-wheel 测试仅检查构造签名和配置字段,未实例化加载器或读取 checkpoint,无法验证真实双 rank 的拆分、过滤、collective 对齐和数值正确性。 - [I] 代码质量 — 删除或重命名内部 file、registry entry、model name、metric enum、op binding、plugin symbol 时,必须全仓搜索消费者,并提供替代实现、迁移说明或 smoke 覆盖;只有暴露到 HTTP/RPC/config/persisted format 时才按外部兼容性处理 → issue
真实 FastSafeTensors 多 rank 加载边界未被回归覆盖
删除私有PerExpertParallelLoader后,rank-local 过滤和 stacked-MoE 拆分交由上游AutoLoader。行为测试替换了整个包,installed-wheel 测试仅检查构造签名和配置字段,未实例化加载器或读取 checkpoint,无法验证真实双 rank 的拆分、过滤、collective 对齐和数值正确性。
Python Static-First Checklist
- [P.G] 测试规范 — mock/fake/stub 不得替代本次声称覆盖的生产边界 → issue
真实 FastSafeTensors 多 rank 加载边界未被回归覆盖
删除私有PerExpertParallelLoader后,rank-local 过滤和 stacked-MoE 拆分交由上游AutoLoader。行为测试替换了整个包,installed-wheel 测试仅检查构造签名和配置字段,未实例化加载器或读取 checkpoint,无法验证真实双 rank 的拆分、过滤、collective 对齐和数值正确性。
Strengths
- 统一了 stacked-MoE 布局解析及 inline FP8 加载接口,减少重复逻辑。
- 显存预算补充了 RTP 常驻开销,并仅对真实 stacked 权重增加额外预算。
- 能力探测、错误分类、降级日志和 CPU 策略测试覆盖较完整。
| return self._load_from_scratch(device) | ||
| try: | ||
| return self._load_from_fastsafetensor(device, stacked_moe_mode) | ||
| except FastSafeTensorsCompatibilityError as error: |
There was a problem hiding this comment.
[P1] 兼容回退时 traceback 会阻止已加载 GPU 权重释放
兼容异常可能在部分权重写入 model_weights 后抛出。当前在 except ... as error 作用域内清理并立即执行 scratch;活动异常及 traceback 仍引用 _load_from_fastsafetensor 帧和其中的 GPU tensor,清理无法释放这些显存,scratch 全量加载可能 OOM。
建议: 在异常块内仅保存字符串化原因,退出异常作用域后再清理显存并执行 scratch;增加部分加载后抛出兼容异常的显存生命周期测试。
Checklist: [6.1] 错误语义:fail-fast/retry/fallback/silent 行为显式
| ) | ||
|
|
||
| py_test( | ||
| name = "ckpt_database_fastsafetensors_contract_test", |
There was a problem hiding this comment.
[P1] wheel 契约测试未限制到支持的平台
该目标没有 target_compatible_with 或平台标签,却固定依赖 //rtp_llm:fastsafetensors 并要求 per-expert。CPU、CUDA 12 和 ROCm 锁未提供该新版依赖,跨平台通配测试会在依赖分析或能力断言阶段失败。
建议: 仅在提供新版 wheel 的配置启用该目标,或按平台拆分依赖和能力预期,并验证全部平台矩阵均可分析执行。
Checklist: [H] BUILD、py_test、cc_test、genrule 变更必须验证 srcs/data/runfiles/testdata 相对路径、import path 和 target 可执行性;genrule glob 不得捕获无关构建产物
| installed_module = sys.modules.get("fastsafetensors") | ||
| self.assertFalse(getattr(installed_module, "_rtp_test_fake", False)) | ||
|
|
||
| def test_available_auto_loader_contract_is_classified(self) -> None: |
There was a problem hiding this comment.
[P1] 真实 FastSafeTensors 多 rank 加载边界未被回归覆盖
删除私有 PerExpertParallelLoader 后,rank-local 过滤和 stacked-MoE 拆分交由上游 AutoLoader。行为测试替换了整个包,installed-wheel 测试仅检查构造签名和配置字段,未实例化加载器或读取 checkpoint,无法验证真实双 rank 的拆分、过滤、collective 对齐和数值正确性。
建议: 在受支持 GPU 平台使用锁定 wheel 和小型 stacked checkpoint,覆盖单 rank、双 rank、rank-local expert 集合、输出数值、资源关闭及峰值显存。
Checklist: [6.1] 分布式/跨平台变更有对应覆盖;[6.1] 新逻辑有聚焦单测 + 相关集成/smoke 测试;[6.1] 被删除测试有等价替代覆盖;[P.G] mock/fake/stub 不得替代本次声称覆盖的生产边界;[H] mock/fake/stub 只能隔离非目标依赖,不能 stub 掉本次声称覆盖的生产边界;涉及 pybind/C++/runtime 的 bug 必须有真实边界集成或 smoke 覆盖;[H] 新增、迁移或删除测试必须证明目标行为仍在 CI 中执行;DISABLED_、#if 0、open_skip、导入即失败、未被 target 引用的用例不得计为覆盖;[I] 删除或重命名内部 file、registry entry、model name、metric enum、op binding、plugin symbol 时,必须全仓搜索消费者,并提供替代实现、迁移说明或 smoke 覆盖;只有暴露到 HTTP/RPC/config/persisted format 时才按外部兼容性处理
| if stacked_moe_mode is None: | ||
| return self._load_from_scratch(device) | ||
| if ( | ||
| stacked_moe_mode == FASTSAFETENSORS_STACKED_MOE_MODE_FULL_STACKED |
There was a problem hiding this comment.
[P2] full-stacked 开关会影响非 stacked checkpoint
显式 FastSafeTensors 只要选择 full-stacked 就执行内存预检,尚未确认 checkpoint 是否包含 raw stacked 权重。密集或原生 per-expert checkpoint 也可能因该环境变量意外回退 scratch,违反该开关仅控制 stacked-MoE 交付方式的文档约定。
建议: 仅在 _has_raw_stacked_moe_weights() 为真时应用 full-stacked 预检;其他 checkpoint 保持显式 per-expert 路径的行为。
Checklist: [6.1] 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全;[6.1] 状态不变量:创建/更新/失败/重试/回滚路径有效
| When a degraded FastSafeTensors mode remains usable, RTP logs | ||
| `requested_mode`, `effective_mode` and `degraded_reason`; rank-local copy-out | ||
| degradation uses the same fields with `effective_mode=consumer-filter`. Every | ||
| scratch fallback contains `falls back to scratch`; package absence is INFO and |
There was a problem hiding this comment.
[P3] scratch fallback 日志级别说明与实现不一致
文档称除包缺失外的 fallback 均为 WARNING,但 AUTO 前置条件不满足时实现使用 INFO。依赖该文档配置 WARNING 监控会漏掉此类回退。
建议: 明确包缺失和正常前置条件不满足均使用 INFO,或将实现调整为文档承诺的 WARNING。
Checklist: [6.1] 可观测性:日志/指标/超时可操作、非噪声
c41fdd2 to
6747631
Compare
LLLLKKKK
left a comment
There was a problem hiding this comment.
AI Code Review - PR #1354
Status: BLOCKING
Summary: P0/0 · P1/4 · P2/0 · P3/0
Reviewed: commit 67476312abe0 · 2026-09-01 20:46 UTC+8
Blocking Issues
P1
- CUDA 12.9 ARM 混用不同架构和版本的 Torch @
arch_config/arch_select.bzl:50- 建议:增加匹配 CUDA 12.9 ARM 的 aarch64 Torch archive,并让
requirement()、torch_deps()和whl_deps()使用同一 Torch/CUDA 版本。
- 建议:增加匹配 CUDA 12.9 ARM 的 aarch64 Torch archive,并让
- CUDA 13 ARM wheel 通过明文 HTTP 获取可执行依赖 @
arch_config/arch_select.bzl:102- 建议:迁移到可信 HTTPS 制品源,并在直接依赖 URL 中附加 SHA256 摘要,确保发布元数据与 lockfile 指向同一受控制品。
- 检查点 I/O 错误不会按文档承诺保持 fail-fast @
rtp_llm/utils/database.py:120- 建议:仅转换具有明确导入、动态库或 ABI 特征的
OSError;让检查点 I/O 异常原样传播,并补充构造和迭代阶段的测试。
- 建议:仅转换具有明确导入、动态库或 ABI 特征的
- 真实 FastSafeTensors 多 rank 加载边界未被回归覆盖 @
rtp_llm/utils/test/ckpt_database_test.py:507- 建议:使用真实 wrapper/native wheels、最小 safetensors 检查点和至少两个 GPU rank,执行构造、迭代、过滤、拆分、广播及关闭流程。
Checklist Findings (5 fail / 121 total)
General Principles Checklist
- [6.1] Architecture — 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全 → issue
检查点 I/O 错误不会按文档承诺保持 fail-fast
_is_fastsafetensors_compatibility_error()将所有OSError视为兼容性错误;构造和迭代阶段的FileNotFoundError、存储读取故障也会被包装,随后ModelLoader回退 scratch。这与 checkpoint/data 错误保持 fail-fast 的文档契约不符,并可能掩盖根因、重复加载模型。 - [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue
检查点 I/O 错误不会按文档承诺保持 fail-fast
_is_fastsafetensors_compatibility_error()将所有OSError视为兼容性错误;构造和迭代阶段的FileNotFoundError、存储读取故障也会被包装,随后ModelLoader回退 scratch。这与 checkpoint/data 错误保持 fail-fast 的文档契约不符,并可能掩盖根因、重复加载模型。 - [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue
真实 FastSafeTensors 多 rank 加载边界未被回归覆盖
安装 wheel 契约测试仅检查AutoLoader签名和load_config();加载、过滤、拆分及异常测试均使用假模块,也未初始化多 rank。删除私有多 rank 加载器后,真实 wrapper/native ABI、rank-local 过滤、stacked-MoE 拆分及广播回归仍可能通过门禁。 - [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue
真实 FastSafeTensors 多 rank 加载边界未被回归覆盖
安装 wheel 契约测试仅检查AutoLoader签名和load_config();加载、过滤、拆分及异常测试均使用假模块,也未初始化多 rank。删除私有多 rank 加载器后,真实 wrapper/native ABI、rank-local 过滤、stacked-MoE 拆分及广播回归仍可能通过门禁。
Python Static-First Checklist
- [P.G] 测试规范 — mock/fake/stub 不得替代本次声称覆盖的生产边界 → issue
真实 FastSafeTensors 多 rank 加载边界未被回归覆盖
安装 wheel 契约测试仅检查AutoLoader签名和load_config();加载、过滤、拆分及异常测试均使用假模块,也未初始化多 rank。删除私有多 rank 加载器后,真实 wrapper/native ABI、rank-local 过滤、stacked-MoE 拆分及广播回归仍可能通过门禁。
Strengths
- CUDA 13 ARM 的独立依赖锁、libtorch archive 与 FlashInfer 路由已接通。
- MoE 布局解析和 FastSafeTensors 能力降级逻辑得到统一。
- 兼容回退前会释放异常 traceback 和 GPU 权重引用。
- 单元测试覆盖模式解析、内存预算、资源关闭及 FP8 加载顺序。
| installed_module = sys.modules.get("fastsafetensors") | ||
| self.assertFalse(getattr(installed_module, "_rtp_test_fake", False)) | ||
|
|
||
| def test_available_auto_loader_contract_is_classified(self) -> None: |
There was a problem hiding this comment.
[P1] 真实 FastSafeTensors 多 rank 加载边界未被回归覆盖
安装 wheel 契约测试仅检查 AutoLoader 签名和 load_config();加载、过滤、拆分及异常测试均使用假模块,也未初始化多 rank。删除私有多 rank 加载器后,真实 wrapper/native ABI、rank-local 过滤、stacked-MoE 拆分及广播回归仍可能通过门禁。
建议: 使用真实 wrapper/native wheels、最小 safetensors 检查点和至少两个 GPU rank,执行构造、迭代、过滤、拆分、广播及关闭流程。
Checklist: [6.1] 分布式/跨平台变更有对应覆盖;[6.1] 新逻辑有聚焦单测 + 相关集成/smoke 测试;[P.G] mock/fake/stub 不得替代本次声称覆盖的生产边界
6747631 to
0bcf271
Compare
LLLLKKKK
left a comment
There was a problem hiding this comment.
AI Code Review - PR #1354
Status: BLOCKING
Summary: P0/0 · P1/1 · P2/1 · P3/0
Reviewed: commit 0bcf271ee1b4 · 2026-09-01 21:29 UTC+8
Blocking Issues
P1
- CUDA 12.9 x86 wheel 元数据与构建依赖不一致 @
arch_config/arch_select.bzl:88- 建议:增加
using_cuda12_9_x86专用 wheel 依赖分支,与 CUDA 12.9 锁文件保持一致,并增加生成 wheel 的Requires-Dist断言。
- 建议:增加
Non-blocking Suggestions
P2
- CUDA 13 ARM 缺少真实 FastSafeTensors 加载覆盖 @
rtp_llm/utils/test/BUILD:144- 建议:为 CUDA 13 ARM 增加真实 wheel 运行目标,至少覆盖单 rank 构造、读取和关闭;具备多卡环境时复用双 rank 拆分、过滤与广播用例。
Checklist Findings (3 fail / 121 total)
General Principles Checklist
- [6.1] Architecture — 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全 → issue
CUDA 12.9 x86 wheel 元数据与构建依赖不一致
CUDA 12.9 x86 的锁文件和 Bazel 依赖使用 Torch 2.8 与 FastSafeTensors 0.3.4,但whl_deps()缺少using_cuda12_9_x86分支,只命中通用using_cuda12,使发布 wheel 仍声明 Torch 2.6 且遗漏 FastSafeTensors。干净环境安装后会得到与编译和运行时契约不一致的依赖。 - [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue
CUDA 13 ARM 缺少真实 FastSafeTensors 加载覆盖
ARM 合约目标仅检查已安装AutoLoader的签名和能力字段;实际构造加载器、读取 checkpoint、执行 collective 并验证关闭语义的目标仅允许 CUDA x86 且固定使用 H20。因此 ARM 专用 wheel 的构造、原生读取和通信生命周期未被真实执行。 - [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue
CUDA 13 ARM 缺少真实 FastSafeTensors 加载覆盖
ARM 合约目标仅检查已安装AutoLoader的签名和能力字段;实际构造加载器、读取 checkpoint、执行 collective 并验证关闭语义的目标仅允许 CUDA x86 且固定使用 H20。因此 ARM 专用 wheel 的构造、原生读取和通信生命周期未被真实执行。
Strengths
- 统一 stacked-MoE 布局解析,并复用于加载和 FP8 量化路径。
- 能力降级、检查点错误语义及资源关闭边界清晰。
- 新增真实双 rank 测试,覆盖拆分、过滤、广播和关闭流程。
- CUDA ARM 依赖已按平台路由并固定版本与摘要。
| "//rtp_llm:utils", | ||
| ], | ||
| exec_properties = {"gpu": "H20", "gpu_count": "2"}, | ||
| target_compatible_with = select({ |
There was a problem hiding this comment.
[P2] CUDA 13 ARM 缺少真实 FastSafeTensors 加载覆盖
ARM 合约目标仅检查已安装 AutoLoader 的签名和能力字段;实际构造加载器、读取 checkpoint、执行 collective 并验证关闭语义的目标仅允许 CUDA x86 且固定使用 H20。因此 ARM 专用 wheel 的构造、原生读取和通信生命周期未被真实执行。
建议: 为 CUDA 13 ARM 增加真实 wheel 运行目标,至少覆盖单 rank 构造、读取和关闭;具备多卡环境时复用双 rank 拆分、过滤与广播用例。
Checklist: [6.1] 分布式/跨平台变更有对应覆盖;[6.1] 新逻辑有聚焦单测 + 相关集成/smoke 测试
LLLLKKKK
left a comment
There was a problem hiding this comment.
AI Code Review - PR #1354
Status: BLOCKING
Summary: P0/0 · P1/1 · P2/3 · P3/1
Reviewed: commit 82e98bfc0d03 · 2026-09-01 23:14 UTC+8
Blocking Issues
P1
- CUDA 13 wheel 元数据变更没有对应发布门禁 @
rtp_llm/utils/test/BUILD:175- 建议:为 CUDA 13 x86、CUDA 13 ARM 分别增加 wheel 元数据目标,并参数化校验对应锁文件中的关键依赖、版本、URL 与哈希。
Non-blocking Suggestions
P2
- CUDA 13 ARM wheel 未固定所需的 ARM z3-solver 构建 @
arch_config/arch_select.bzl:108- 建议:将锁文件中的 ARM
z3-solver直接依赖及哈希加入该 wheel 分支,并验证干净 ARM 环境的安装与 TileLang 加载。
- 建议:将锁文件中的 ARM
- 新增及更新的 wheel 二进制依赖缺少哈希约束 @
arch_config/arch_select.bzl:108- 建议:将对应锁文件中的哈希附加到本次新增或更新的所有直接 URL 依赖。
full-stacked内存预检被描述为无条件执行 @docs/backend/server_arguments.md:218- 建议:明确说明显式
full-stacked仅对 raw stacked MoE checkpoint 执行预检;若预期始终预检,则同步调整实现和测试。
- 建议:明确说明显式
P3
- full-stacked 的上游 rank-local 过滤谓词转发分支未被测试命中 @
rtp_llm/utils/test/ckpt_database_test.py:294- 建议:让 fake 显式声明并执行
local_copyout_filter,断言 raw stacked key 被接纳且展开后仅保留本 rank expert。
- 建议:让 fake 显式声明并执行
Checklist Findings (5 fail / 121 total)
General Principles Checklist
- [6.1] Architecture — 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全 → issue ``full-stacked
内存预检被描述为无条件执行
文档称显式 `full-stacked` 会保留内存预检,但实现仅在检测到 raw stacked MoE 权重时执行;dense 或已按 expert 存储的 checkpoint 会完全跳过预检,文档后文也给出了不同限定。 - [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue
CUDA 13 ARM wheel 未固定所需的 ARM z3-solver 构建
CUDA 13 ARM requirements 与锁文件显式固定专用z3-solver,TileLang 及运行时代码依赖其libz3;但 wheel 分支只直接加入 TileLang。安装 wheel 时可能从软件源解析到未经该平台验证的 z3 构建。 - [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue
full-stacked 的上游 rank-local 过滤谓词转发分支未被测试命中
该测试的 FakeAutoLoader 仅声明**kwargs,能力探测不会将其视为支持local_copyout_filter,因此只覆盖 consumer-filter 路径,未执行向 AutoLoader 转发扩展后过滤谓词的分支;真实双卡测试固定使用 per-expert。
RTP-LLM Checklist
- [H] 测试与 CI — mock/fake/stub 只能隔离非目标依赖,不能 stub 掉本次声称覆盖的生产边界;涉及 pybind/C++/runtime 的 bug 必须有真实边界集成或 smoke 覆盖 → issue
full-stacked 的上游 rank-local 过滤谓词转发分支未被测试命中
该测试的 FakeAutoLoader 仅声明**kwargs,能力探测不会将其视为支持local_copyout_filter,因此只覆盖 consumer-filter 路径,未执行向 AutoLoader 转发扩展后过滤谓词的分支;真实双卡测试固定使用 per-expert。 - [H] 测试与 CI — 新增、迁移或删除测试必须证明目标行为仍在 CI 中执行;DISABLED_、#if 0、open_skip、导入即失败、未被 target 引用的用例不得计为覆盖 → issue
CUDA 13 wheel 元数据变更没有对应发布门禁
唯一的 wheel 元数据测试仅在 CUDA 12.9 x86 生效。本次 CUDA 13 x86 和 ARM 均变更了平台专属依赖,但其最终Requires-Dist未被构建或校验,错误的平台、ABI、URL 或哈希可通过发布流程,直至用户安装时暴露。
Strengths
- 统一了 MoE 布局解析与在线 FP8 加载契约。
- FastSafeTensors 能力探测、降级及资源清理覆盖较完整。
- 新增 x86、ARM 双卡真实集成测试,覆盖按秩过滤、广播与存储所有权。
- CUDA 12.9 wheel 依赖已与锁文件对齐并增加元数据测试。
| }), | ||
| ) | ||
|
|
||
| py_test( |
There was a problem hiding this comment.
[P1] CUDA 13 wheel 元数据变更没有对应发布门禁
唯一的 wheel 元数据测试仅在 CUDA 12.9 x86 生效。本次 CUDA 13 x86 和 ARM 均变更了平台专属依赖,但其最终 Requires-Dist 未被构建或校验,错误的平台、ABI、URL 或哈希可通过发布流程,直至用户安装时暴露。
建议: 为 CUDA 13 x86、CUDA 13 ARM 分别增加 wheel 元数据目标,并参数化校验对应锁文件中的关键依赖、版本、URL 与哈希。
Checklist: [H] 新增、迁移或删除测试必须证明目标行为仍在 CI 中执行;DISABLED_、#if 0、open_skip、导入即失败、未被 target 引用的用例不得计为覆盖
| "fastsafetensors@https://rtp-maga.oss-cn-zhangjiakou.aliyuncs.com/0502/fastsafetensors-0.1.20%2Bali-cp310-cp310-linux_x86_64.whl", | ||
| "fastsafetensors@https://rtp-opensource.oss-cn-hangzhou.aliyuncs.com/rtp_llm/cu130/fastsafetensors-0.3.4.dev20260901%2Bali.fuseshm.g78ac75c8.aone67880226-cp310-cp310-linux_x86_64.whl", | ||
| ], | ||
| "@rtp_llm//:using_cuda13_arm": [ |
There was a problem hiding this comment.
[P2] CUDA 13 ARM wheel 未固定所需的 ARM z3-solver 构建
CUDA 13 ARM requirements 与锁文件显式固定专用 z3-solver,TileLang 及运行时代码依赖其 libz3;但 wheel 分支只直接加入 TileLang。安装 wheel 时可能从软件源解析到未经该平台验证的 z3 构建。
建议: 将锁文件中的 ARM z3-solver 直接依赖及哈希加入该 wheel 分支,并验证干净 ARM 环境的安装与 TileLang 加载。
Checklist: [6.1] 分布式/跨平台变更有对应覆盖
| "fastsafetensors@https://rtp-maga.oss-cn-zhangjiakou.aliyuncs.com/0502/fastsafetensors-0.1.20%2Bali-cp310-cp310-linux_x86_64.whl", | ||
| "fastsafetensors@https://rtp-opensource.oss-cn-hangzhou.aliyuncs.com/rtp_llm/cu130/fastsafetensors-0.3.4.dev20260901%2Bali.fuseshm.g78ac75c8.aone67880226-cp310-cp310-linux_x86_64.whl", | ||
| ], | ||
| "@rtp_llm//:using_cuda13_arm": [ |
There was a problem hiding this comment.
[P2] 新增及更新的 wheel 二进制依赖缺少哈希约束
CUDA 13 ARM 新增的多数直接二进制依赖未携带锁文件已有的 SHA-256,CUDA 13 x86 更新后的 fastsafetensors 也缺失哈希。远端对象变化时,wheel 安装无法拒绝与构建环境不一致的产物。
建议: 将对应锁文件中的哈希附加到本次新增或更新的所有直接 URL 依赖。
| consumer-side filtering. A missing package/`AutoLoader`, an import/ABI failure, | ||
| an unmet AUTO prerequisite, or an insufficient AUTO memory preflight falls back | ||
| to `scratch`. Explicit `LOAD_METHOD=fastsafetensors` treats `per-expert` as a | ||
| user override and skips the memory preflight; explicit `full-stacked` keeps the |
There was a problem hiding this comment.
[P2] full-stacked 内存预检被描述为无条件执行
文档称显式 full-stacked 会保留内存预检,但实现仅在检测到 raw stacked MoE 权重时执行;dense 或已按 expert 存储的 checkpoint 会完全跳过预检,文档后文也给出了不同限定。
建议: 明确说明显式 full-stacked 仅对 raw stacked MoE checkpoint 执行预检;若预期始终预检,则同步调整实现和测试。
Checklist: [6.1] 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全
| source_tensor.untyped_storage().data_ptr(), | ||
| ) | ||
|
|
||
| def test_full_stacked_mode_clones_only_rank_local_experts(self) -> None: |
There was a problem hiding this comment.
[P3] full-stacked 的上游 rank-local 过滤谓词转发分支未被测试命中
该测试的 FakeAutoLoader 仅声明 **kwargs,能力探测不会将其视为支持 local_copyout_filter,因此只覆盖 consumer-filter 路径,未执行向 AutoLoader 转发扩展后过滤谓词的分支;真实双卡测试固定使用 per-expert。
建议: 让 fake 显式声明并执行 local_copyout_filter,断言 raw stacked key 被接纳且展开后仅保留本 rank expert。
Checklist: [6.1] 新逻辑有聚焦单测 + 相关集成/smoke 测试;[H] mock/fake/stub 只能隔离非目标依赖,不能 stub 掉本次声称覆盖的生产边界;涉及 pybind/C++/runtime 的 bug 必须有真实边界集成或 smoke 覆盖
LLLLKKKK
left a comment
There was a problem hiding this comment.
AI Code Review - PR #1354
Status: BLOCKING
Summary: P0/0 · P1/4 · P2/4 · P3/0
Reviewed: commit 5148cce29d36 · 2026-09-02 00:08 UTC+8
Blocking Issues
P1
- CUDA 12 ARM 专属依赖仓库缺少构建所需包 @
arch_config/arch_select.bzl:50- 建议:补齐该平台实际支持的 requirements/lock 制品,或从 CUDA 12 ARM BUILD 分支移除未发布依赖,并增加对应配置的 Bazel 分析和 wheel 构建测试。
- CUDA 12.9 wheel 强制安装与 Torch/CUDA 不匹配的原生扩展 @
arch_config/arch_select.bzl:96- 建议:提供针对 Torch 2.8+cu129 构建的扩展 wheel,同步 requirements、lock 与
whl_deps(),并在干净环境中验证安装、导入和真实 AutoLoader 加载。
- 建议:提供针对 Torch 2.8+cu129 构建的扩展 wheel,同步 requirements、lock 与
- 清理阶段的兼容性异常不会按文档回退 @
rtp_llm/utils/database.py:573- 建议:对
close()应用相同的兼容性分类,并在存在主异常时保留主异常;补充缺失及失败close()的回退测试。
- 建议:对
- CUDA 12 ARM wheel 依赖分支缺少元数据回归测试 @
rtp_llm/utils/test/BUILD:175- 建议:增加 CUDA 12 ARM 期望映射和对应 Bazel 测试目标。
Non-blocking Suggestions
P2
- wheel 元数据测试未验证锁文件与平台依赖全集 @
rtp_llm/utils/test/rtp_llm_wheel_metadata_test.py:136- 建议:解析平台锁文件或从单一清单生成两侧数据,并对平台敏感直接依赖执行集合全等校验。
- 真实多卡测试未覆盖 UE8M0 广播兼容路径 @
rtp_llm/utils/test/ckpt_database_fastsafetensors_multirank_test.py:108- 建议:增加 UE8M0 双 rank 广播场景,按生产入口加载补丁,并逐 rank 校验 dtype 与原始字节。
- 双 rank 用例未跨越配置的分块边界 @
rtp_llm/utils/test/ckpt_database_fastsafetensors_multirank_test.py:43- 建议:加入超过 64 KiB 的张量或累计超限的多个张量,并保留按秩键值及关闭后存储有效性断言。
- wheel 元数据测试在模块导入阶段修改命令行参数 @
rtp_llm/utils/test/rtp_llm_wheel_metadata_test.py:10- 建议:将参数解析移入主入口,并显式传递平台和 wheel 路径。
Checklist Findings (10 fail / 121 total)
General Principles Checklist
- [6.1] Architecture — 依赖方向:无循环依赖/跨层惊喜 → issue
CUDA 12 ARM 专属依赖仓库缺少构建所需包
requirement()将 CUDA 12 ARM 全部路由到专属锁文件,但该锁缺少下游 BUILD 在此配置选择的deep_gemm、deep_ep、rtp-kernel、FlashAttention 及多个 FlashInfer 缓存包。分析这些目标时对应 pip 仓库标签不存在,会阻断 CUDA 12 ARM 构建。 - [6.1] Architecture — 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全 → issue
清理阶段的兼容性异常不会按文档回退
导入、构造和迭代异常会转换为FastSafeTensorsCompatibilityError,但finally直接调用loader.close()。缺失或不兼容的close()会原样逃逸并绕过ModelLoader的 scratch 回退;若已有迭代异常,清理异常还会覆盖原异常。 - [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue
清理阶段的兼容性异常不会按文档回退
导入、构造和迭代异常会转换为FastSafeTensorsCompatibilityError,但finally直接调用loader.close()。缺失或不兼容的close()会原样逃逸并绕过ModelLoader的 scratch 回退;若已有迭代异常,清理异常还会覆盖原异常。 - [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue
清理阶段的兼容性异常不会按文档回退
导入、构造和迭代异常会转换为FastSafeTensorsCompatibilityError,但finally直接调用loader.close()。缺失或不兼容的close()会原样逃逸并绕过ModelLoader的 scratch 回退;若已有迭代异常,清理异常还会覆盖原异常。 - [6.1] Software Engineering — DRY:重复非平凡逻辑被抽取或显式复用 → issue
wheel 元数据测试未验证锁文件与平台依赖全集
名为match_platform_lock_file的测试未读取锁文件,只比较手写期望子集,也不拒绝额外的平台敏感依赖。锁文件与测试复制相同错误,或 wheel 混入其他平台依赖时仍会通过。 - [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue
真实多卡测试未覆盖 UE8M0 广播兼容路径
checkpoint 仅包含 float32 张量,测试也未按生产入口加载torch_patch,因此新增的 UE8M0 到 uint8 广播兼容逻辑从未触发;该 shim 失效时双卡测试仍会通过。 - [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue
真实多卡测试未覆盖 UE8M0 广播兼容路径
checkpoint 仅包含 float32 张量,测试也未按生产入口加载torch_patch,因此新增的 UE8M0 到 uint8 广播兼容逻辑从未触发;该 shim 失效时双卡测试仍会通过。 - [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue
双 rank 用例未跨越配置的分块边界
I/O 与 batch 上限均为 64 KiB,但 checkpoint 仅包含 10 个 float32 元素。虽然启用了chunked_loading,实际未覆盖多 chunk 切换、刷新或跨 chunk 存储生命周期。
RTP-LLM Checklist
- [I] 代码质量 — 同一功能用统一工具函数 → issue
wheel 元数据测试未验证锁文件与平台依赖全集
名为match_platform_lock_file的测试未读取锁文件,只比较手写期望子集,也不拒绝额外的平台敏感依赖。锁文件与测试复制相同错误,或 wheel 混入其他平台依赖时仍会通过。
Python Static-First Checklist
- [P.F] 语言陷阱 — 禁止模块级 import 副作用 → issue
wheel 元数据测试在模块导入阶段修改命令行参数
模块导入时无条件执行两次sys.argv.pop(1)。其他测试发现器或导入工具可能触发IndexError,也可能意外消费调用方参数。
Strengths
- 统一 MoE 布局解析,减少普通加载与在线量化路径漂移。
- FastSafeTensors 能力降级、显存预检和 scratch 回滚设计清晰。
- CUDA 13 wheel 元数据、哈希固定及 ARM 专属依赖已有平台级校验。
- 新增真实双卡测试,覆盖按秩过滤、广播和存储所有权。
| # installing the RTP-LLM wheel cannot silently select another ABI. | ||
| "torch@https://rtp-opensource.oss-cn-hangzhou.aliyuncs.com/rtp_llm/cu129/torch-2.8.0%2Bcu129-cp310-cp310-manylinux_2_28_x86_64.whl#sha256=54d240b5d3b1f9075d4ee6179675a22c1974f7bef1885d134c582678d5180cd3", | ||
| "torchvision@https://rtp-opensource.oss-cn-hangzhou.aliyuncs.com/rtp_llm/cu129/torchvision-0.23.0%2Bcu129-cp310-cp310-manylinux_2_28_x86_64.whl#sha256=5690810877f2d7d1a2b432e31d68d4a9ccbb695a9a8fa0e27bbad44c6a90a181", | ||
| "fast-safetensors@https://rtp-opensource.oss-cn-hangzhou.aliyuncs.com/rtp_llm/cu129/fast_safetensors-0.7.3%2Btorch2.1.2.cu121-cp310-cp310-linux_x86_64.whl#sha256=dd760931feb6dd585cc0b14b1bacf39c1e31a39bb9f8e12a8c62720caf1ccc1f", |
There was a problem hiding this comment.
[P1] CUDA 12.9 wheel 强制安装与 Torch/CUDA 不匹配的原生扩展
CUDA 12.9 wheel 固定 Torch 2.8+cu129,却同时安装文件名明确针对 Torch 2.1.2+cu121 构建的 fast-safetensors 原生扩展。默认 FuseShm 路径会加载该扩展,可能产生 ABI 错误并导致快速加载退化或启动失败。
建议: 提供针对 Torch 2.8+cu129 构建的扩展 wheel,同步 requirements、lock 与 whl_deps(),并在干净环境中验证安装、导入和真实 AutoLoader 加载。
| }), | ||
| ) | ||
|
|
||
| py_test( |
There was a problem hiding this comment.
[P1] CUDA 12 ARM wheel 依赖分支缺少元数据回归测试
whl_deps() 新增独立的 using_cuda12_arm 分支,但期望表和 BUILD 目标仅覆盖 CUDA 12.9 x86、CUDA 13 x86 与 CUDA 13 ARM。该平台的依赖缺失、哈希错误或架构漂移不会被此发布门禁发现。
建议: 增加 CUDA 12 ARM 期望映射和对应 Bazel 测试目标。
|
|
||
|
|
||
| class RtpLlmWheelMetadataTest(unittest.TestCase): | ||
| def test_direct_requirements_match_platform_lock_file(self) -> None: |
There was a problem hiding this comment.
[P2] wheel 元数据测试未验证锁文件与平台依赖全集
名为 match_platform_lock_file 的测试未读取锁文件,只比较手写期望子集,也不拒绝额外的平台敏感依赖。锁文件与测试复制相同错误,或 wheel 混入其他平台依赖时仍会通过。
建议: 解析平台锁文件或从单一清单生成两侧数据,并对平台敏感直接依赖执行集合全等校验。
Checklist: [6.1] DRY:重复非平凡逻辑被抽取或显式复用;[I] 同一功能用统一工具函数
| ) | ||
| with tempfile.TemporaryDirectory() as tmp_dir: | ||
| checkpoint_path = os.path.join(tmp_dir, "model.safetensors") | ||
| save_file( |
There was a problem hiding this comment.
[P2] 真实多卡测试未覆盖 UE8M0 广播兼容路径
checkpoint 仅包含 float32 张量,测试也未按生产入口加载 torch_patch,因此新增的 UE8M0 到 uint8 广播兼容逻辑从未触发;该 shim 失效时双卡测试仍会通过。
建议: 增加 UE8M0 双 rank 广播场景,按生产入口加载补丁,并逐 rank 校验 dtype 与原始字节。
Checklist: [6.1] 分布式/跨平台变更有对应覆盖;[6.1] 新逻辑有聚焦单测 + 相关集成/smoke 测试
| # the integration test cheap. | ||
| os.environ["FASTSAFETENSORS_NOGDS"] = "0" | ||
| os.environ.pop("FASTSAFETENSORS_CONFIG", None) | ||
| os.environ["FASTSAFETENSORS_CONFIG_JSON"] = json.dumps( |
There was a problem hiding this comment.
[P2] 双 rank 用例未跨越配置的分块边界
I/O 与 batch 上限均为 64 KiB,但 checkpoint 仅包含 10 个 float32 元素。虽然启用了 chunked_loading,实际未覆盖多 chunk 切换、刷新或跨 chunk 存储生命周期。
建议: 加入超过 64 KiB 的张量或累计超限的多个张量,并保留按秩键值及关闭后存储有效性断言。
Checklist: [6.1] 边界 case 覆盖(空、单元素、最大值)
LLLLKKKK
left a comment
There was a problem hiding this comment.
AI Code Review - PR #1354
Status: BLOCKING
Summary: P0/0 · P1/4 · P2/0 · P3/0
Reviewed: commit 162e7fa658a4 · 2026-09-02 01:15 UTC+8
Blocking Issues
P1
- CUDA 12 ARM 专属依赖仓库缺少构建所需包 @
arch_config/arch_select.bzl:50- 建议:在
requirement()中接入pip_cuda12_arm_torch,补齐 ARM 锁文件所需包,并为torch_deps()注册匹配的 CUDA 12 AArch64 Torch;否则移除该配置及对应 CI。
- 建议:在
- CUDA 12 ARM wheel 依赖分支缺少元数据回归测试 @
rtp_llm/utils/test/BUILD:179- 建议:增加独立的
using_cuda12_armwheel 依赖分支,并添加基于requirements_lock_cuda12_arm.txt的元数据测试,校验平台、架构、URL 和哈希全集。
- 建议:增加独立的
- CUDA 12.9 wheel 强制安装与 Torch/CUDA 不匹配的原生扩展 @
arch_config/arch_select.bzl:94- 建议:提供针对 Torch 2.8/CUDA 12.9 重编译的制品,同步更新源需求、锁文件和 wheel 元数据,并用最终 wheel 执行原生导入及真实
AutoLoader加载测试。
- 建议:提供针对 Torch 2.8/CUDA 12.9 重编译的制品,同步更新源需求、锁文件和 wheel 元数据,并用最终 wheel 执行原生导入及真实
- 固定 2 GiB 预留无法约束 stacked-MoE 权重拼装峰值 @
rtp_llm/model_loader/loader.py:465- 建议:按本 rank 专家数量、形状、dtype 和 TP 切分计算 RTP 侧重叠上界;完成量测前至少保留旧预算下界,并增加组件峰值超过 2 GiB 的 stacked-MoE 预检测试。
Checklist Findings (5 fail / 121 total)
General Principles Checklist
- [6.1] Architecture — 依赖方向:无循环依赖/跨层惊喜 → issue
CUDA 12 ARM 专属依赖仓库缺少构建所需包
cuda12_9_arm设置using_cuda12_arm=true,因此不匹配要求该值为 false 的cuda_pre_12_9。requirement()没有 CUDA 12 ARM 分支,会落到 CPU 仓库;torch_deps()同样落到 x86 CPU Torch。CPU 锁中不存在 BUILD 请求的fast-safetensors、fastsafetensors等目标,保留的 ARM CI 配置会分析失败或选错 Torch。 - [6.1] Architecture — 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全 → issue
CUDA 12.9 wheel 强制安装与 Torch/CUDA 不匹配的原生扩展
CUDA 12.9 wheel 同时固定 Torch 2.8+cu129 与文件名明确标记为 Torch 2.1.2+cu121 的fast-safetensors原生扩展。锁文件和元数据测试复制了相同组合,只能验证声明一致,无法保证 Torch/CUDA ABI;干净安装后导入或 FuseShm 加载可能因符号不兼容失败。 - [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue
固定 2 GiB 预留无法约束 stacked-MoE 权重拼装峰值
任意正数estimated_peak_device_bytes都会取代旧的3 * max_file_size下界,并仅增加固定 2 GiB。TensorCollector会保留一个 rank-local MoE 组件的全部专家张量,weight.load又分配最终合并张量,完成后才清空 collector。该重叠随专家数、形状和 dtype 增长,可超过 2 GiB,导致 AUTO 误判并在启动时 OOM。 - [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue
CUDA 12 ARM wheel 依赖分支缺少元数据回归测试
当前仅定义 CUDA 12.9 x86、CUDA 13 x86 和 CUDA 13 ARM 的 wheel 元数据测试。CUDA 12 ARM 仍是保留的构建配置,却没有对应测试;其whl_deps()会命中通用using_cuda12分支,写入 x86 CUDA 12.6 Torch 且缺少 FastSafeTensors 直接依赖,现有测试无法发现该错误。 - [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue
固定 2 GiB 预留无法约束 stacked-MoE 权重拼装峰值
任意正数estimated_peak_device_bytes都会取代旧的3 * max_file_size下界,并仅增加固定 2 GiB。TensorCollector会保留一个 rank-local MoE 组件的全部专家张量,weight.load又分配最终合并张量,完成后才清空 collector。该重叠随专家数、形状和 dtype 增长,可超过 2 GiB,导致 AUTO 误判并在启动时 OOM。
Strengths
AutoLoader能力协商、兼容性降级和数据错误 fail-fast 语义清晰。- MoE 布局解析集中到统一入口,减少普通与在线 FP8 路径的判断偏差。
- 真实双 rank 测试覆盖分片、rank-local copyout、UE8M0 广播和 FuseShm 分块边界。
- wheel 元数据测试已覆盖 CUDA 12.9 x86、CUDA 13 x86 和 CUDA 13 ARM。
| "@rtp_llm//:using_cuda12_9_x86": [requirement_gpu_cuda12_9(name)], | ||
| "@rtp_llm//:using_cuda13_arm": cuda13_arm_deps, | ||
| "@rtp_llm//:using_rocm": [requirement_gpu_rocm(name)], | ||
| "@rtp_llm//:using_arm": [requirement_arm(name)], |
There was a problem hiding this comment.
[P1] CUDA 12 ARM 专属依赖仓库缺少构建所需包
cuda12_9_arm 设置 using_cuda12_arm=true,因此不匹配要求该值为 false 的 cuda_pre_12_9。requirement() 没有 CUDA 12 ARM 分支,会落到 CPU 仓库;torch_deps() 同样落到 x86 CPU Torch。CPU 锁中不存在 BUILD 请求的 fast-safetensors、fastsafetensors 等目标,保留的 ARM CI 配置会分析失败或选错 Torch。
建议: 在 requirement() 中接入 pip_cuda12_arm_torch,补齐 ARM 锁文件所需包,并为 torch_deps() 注册匹配的 CUDA 12 AArch64 Torch;否则移除该配置及对应 CI。
Checklist: [6.1] 依赖方向:无循环依赖/跨层惊喜
| }), | ||
| ) | ||
|
|
||
| py_test( |
There was a problem hiding this comment.
[P1] CUDA 12 ARM wheel 依赖分支缺少元数据回归测试
当前仅定义 CUDA 12.9 x86、CUDA 13 x86 和 CUDA 13 ARM 的 wheel 元数据测试。CUDA 12 ARM 仍是保留的构建配置,却没有对应测试;其 whl_deps() 会命中通用 using_cuda12 分支,写入 x86 CUDA 12.6 Torch 且缺少 FastSafeTensors 直接依赖,现有测试无法发现该错误。
建议: 增加独立的 using_cuda12_arm wheel 依赖分支,并添加基于 requirements_lock_cuda12_arm.txt 的元数据测试,校验平台、架构、URL 和哈希全集。
Checklist: [6.1] 分布式/跨平台变更有对应覆盖
| # installing the RTP-LLM wheel cannot silently select another ABI. | ||
| "torch@https://rtp-opensource.oss-cn-hangzhou.aliyuncs.com/rtp_llm/cu129/torch-2.8.0%2Bcu129-cp310-cp310-manylinux_2_28_x86_64.whl#sha256=54d240b5d3b1f9075d4ee6179675a22c1974f7bef1885d134c582678d5180cd3", | ||
| "torchvision@https://rtp-opensource.oss-cn-hangzhou.aliyuncs.com/rtp_llm/cu129/torchvision-0.23.0%2Bcu129-cp310-cp310-manylinux_2_28_x86_64.whl#sha256=5690810877f2d7d1a2b432e31d68d4a9ccbb695a9a8fa0e27bbad44c6a90a181", | ||
| "fast-safetensors@https://rtp-opensource.oss-cn-hangzhou.aliyuncs.com/rtp_llm/cu129/fast_safetensors-0.7.3%2Btorch2.1.2.cu121-cp310-cp310-linux_x86_64.whl#sha256=dd760931feb6dd585cc0b14b1bacf39c1e31a39bb9f8e12a8c62720caf1ccc1f", |
There was a problem hiding this comment.
[P1] CUDA 12.9 wheel 强制安装与 Torch/CUDA 不匹配的原生扩展
CUDA 12.9 wheel 同时固定 Torch 2.8+cu129 与文件名明确标记为 Torch 2.1.2+cu121 的 fast-safetensors 原生扩展。锁文件和元数据测试复制了相同组合,只能验证声明一致,无法保证 Torch/CUDA ABI;干净安装后导入或 FuseShm 加载可能因符号不兼容失败。
建议: 提供针对 Torch 2.8/CUDA 12.9 重编译的制品,同步更新源需求、锁文件和 wheel 元数据,并用最终 wheel 执行原生导入及真实 AutoLoader 加载测试。
Checklist: [6.1] 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全
| ) | ||
| budget = legacy_budget | ||
| else: | ||
| budget = int(estimate) |
There was a problem hiding this comment.
[P1] 固定 2 GiB 预留无法约束 stacked-MoE 权重拼装峰值
任意正数 estimated_peak_device_bytes 都会取代旧的 3 * max_file_size 下界,并仅增加固定 2 GiB。TensorCollector 会保留一个 rank-local MoE 组件的全部专家张量,weight.load 又分配最终合并张量,完成后才清空 collector。该重叠随专家数、形状和 dtype 增长,可超过 2 GiB,导致 AUTO 误判并在启动时 OOM。
建议: 按本 rank 专家数量、形状、dtype 和 TP 切分计算 RTP 侧重叠上界;完成量测前至少保留旧预算下界,并增加组件峰值超过 2 GiB 的 stacked-MoE 预检测试。
Checklist: [6.1] 状态不变量:创建/更新/失败/重试/回滚路径有效;[6.1] 边界 case 覆盖(空、单元素、最大值)
Why
RTP should use the config-driven FastSafeTensors AutoLoader as the single bulk-loading entry point while avoiding post-broadcast materialization for tensors that the local rank cannot consume.
What changed
Review boundary
Validation