Conversation
- Add RTP_LLM_CHECK_WITH_INFO for kv_lora_rank alignment in MLAKVCacheSpec.h - Use rtp_llm::RTPException instead of std::exception in test assertion - Add MLAKVCacheSpecTest fixture to reset user_ft_core_dump_on_exception - Declare static_config dependency in BUILD Verified locally with --config=rocm, all 4 tests pass. leader(zhiqchen) on leave, changes made per code owner LLLLKKKK review recommendation.
…baba main CI files
LLLLKKKK
left a comment
There was a problem hiding this comment.
AI Code Review - PR #1330
Status: LGTM
Summary: P0/0 · P1/0 · P2/13 · P3/8
Reviewed: commit 3b7659e3441d · 2026-08-25 15:14 UTC+8
lgtm ready to ci
Non-blocking Suggestions
P2
- enable_env 在两个同名入口语义分叉,EnvArgumentParser 层因未向委托 group 透传而静默失效 @
rtp_llm/server/server_args/server_args.py:229- 建议:将
enable_env与env_name显式下传给委托调用(self._optionals.add_argument(*args, enable_env=enable_env, env_name=env_name, **kwargs))并去掉外层重复的_register_env_mapping;或直接删除EnvArgumentParser.add_argument上这个当前无业务调用方的参数(YAGNI),只保留EnvArgumentGroup一个可信入口。若保留,请补一条单测:直接在 parser 上注册enable_env=False参数,断言其 dest 不出现在get_env_mappings()中。
- 建议:将
- 仅 CLI 通道对运维完全不可见——无任何告警,env→CLI 生成脚本永不产出该参数,最终报错误导根因 @
rtp_llm/server/server_args/model_group_args.py:92- 建议:在
parse_args完成后对声明enable_env=False的 dest 做显式检查:若EXTERNAL_MODEL_PACKAGES/RTP_LLM_EXTERNAL_MODEL_PACKAGES存在于os.environ而 CLI 未传,输出logging.warning明确提示「该参数出于安全考虑仅支持命令行配置,环境变量已被忽略」,并在test_environment_variables_are_ignored中用assertLogs断言该告警,把「静默」变为「可观测的拒绝」。help 文案补一句该参数会在启动时 import 并执行指定包的代码。同时在 PR description 或部署文档中说明:纯 env 部署下如何在启动命令追加该参数、回滚手段(去掉该 flag 即完全不生效),并确认load_external_model_packages的 INFO 日志在各角色进程都会输出。
- 建议:在
- 采用该仅 CLI 参数会翻转 has_cmd_args 分支,使其他所有 env 参数的畸形值从 fail-fast 退化为静默取默认值 @
rtp_llm/server/server_args/server_args.py:268- 建议:把 :373 的
except (ValueError, TypeError): pass与 :371 的ArgumentTypeError分支统一为self.error(...),让两种解析模式对畸形 env 值都 fail-fast;若为兼容性需保留,至少补logging.warning记录被丢弃的env_name与原值。并补一个混合场景测试:sys.argv只带--external_model_packages,同时用 env 设MODEL_TYPE与一个畸形MAX_SEQ_LEN,断言 env 项正确绑定且畸形值不被静默吞掉。
- 建议:把 :373 的
- 仅 CLI 保证依赖类级 _env_mappings 的「缺失」而非显式拒绝 @
rtp_llm/server/server_args/server_args.py:178- 建议:在
EnvArgumentParser上新增_env_disabled_dests: Set[str],enable_env=False时写入该集合;_register_env_mapping遇到已在集合中的 dest 直接跳过并告警,:276 与 :350 两处 env 读取回路也跳过该集合。这样「仅 CLI」从依赖遗漏变为正向拒绝,不再依赖全局状态的洁净程度。并补一条断言assertNotIn("external_model_packages", parser.get_env_mappings())的单测,对机制而非结果建立护栏。
- 建议:在
- ROCm 稀疏 fp8 紧凑布局的 576 字节/token 与仓内 fp8 MLA 布局不吻合,且当前无任何读写方可验证 @
rtp_llm/cpp/cache/MLAKVCacheSpec.h:48- 建议:合并前请补充证据:明确目标 ROCm 内核确实按
[512B fp8 nope | 64B fp8 rope]读写且不需要 scale 区。若 rope 实际仍为 bf16,应写成no_pe + rope * 2;若需要 scale 区应显式计算而非复用非 fp8 分支的表达式。注意spec->block_size_bytes()会直接成为kv_block_stride_bytes,而 DeepSeek V3.2(deepseek_v2.py:711设is_sparse = True,kv_lora_rank=512)在 ROCm 构建下已可触达该分支,一旦有 kernel 沿用 656 字节假设即为越界写。若 ROCm MLA 尚未落地,建议把该分支拆出本 PR、与 ROCm sparse MLA 读写实现一并提交;若保留,请在MLAKVCacheSpec.h:46上方注释说明「为何不需要 scale 区、rope 为何按 1 字节存」,并在build()成功后按 tag 打一条 INFO 记录最终布局与elems_per_token。
- 建议:合并前请补充证据:明确目标 ROCm 内核确实按
- kv_lora_rank 128 对齐硬校验对全平台生效,未对齐配置由静默过量分配变为启动失败且无回退开关 @
rtp_llm/cpp/cache/MLAKVCacheSpec.h:52- 建议:请确认线上是否存在 MLA + fp8 且
kv_lora_rank % 128 != 0的模型(含未进入本仓的外部模型)。若存在,建议改为向上取整(no_pe + 127) / 128 * 4,保持「不小于实际需求」的分配语义而非直接拒绝;若确实要 fail-fast,请在错误信息中给出可行动的处置建议(改用 bf16 kv cache,或用 opaque spec 覆盖),并在 PR description 中说明受影响模型集为空的依据与回滚手段。
- 建议:请确认线上是否存在 MLA + fp8 且
- 外部包加载的两个生产接入点零测试覆盖,单测把 import 边界整体 mock @
rtp_llm/utils/test/import_util_test.py:8- 建议:补一个不依赖 mock 的用例:用临时目录造一个真实包(
__init__.py内调用register_model(..., support_architectures=["FakeArch"])),加入sys.path后调用load_external_model_packages,断言ModelDict.get_ft_model_type_by_hf_architectures("FakeArch")返回预期名;并对setup_default_args补一个用例,让包在 import 时写时序标记,断言其早于model_type推断。同时可参考server_args_test.py:76对pd_separation_config的写法,为ModelArgs.external_model_packages补一次 pickle 往返断言,保护 spawn 子进程的字段传递。
- 建议:补一个不依赖 mock 的用例:用临时目录造一个真实包(
- 新增 CLI 测试类偏离同文件已注释说明的 addCleanup 隔离约定 @
rtp_llm/server/server_args/test/server_args_test.py:539- 建议:对齐
ServerArgsGrammarConfigTest:在修改全局状态之前注册self.addCleanup(_restore),并在_setup()内先importlib.reload(rtp_llm.server.server_args.server_args)以复位类级_env_mappings。更彻底的做法是把 environ/argv 隔离提取为文件内共享 mixin 或基类,让三个测试类复用同一实现,避免继续分叉。
- 建议:对齐
- env 免疫性用例只覆盖两条注入路径中的一条,且只断言结果未验证机制 @
rtp_llm/server/server_args/test/server_args_test.py:603- 建议:补直接断言
self.assertNotIn("external_model_packages", parser.get_env_mappings()),对机制而非结果建立回归护栏。再补一个混合用例:sys.argv = ["prog", "--model_type", "qwen"]同时设置EXTERNAL_MODEL_PACKAGES,断言前者正确生效而后者仍为None,以覆盖 :350 的回填循环;并加一个正向对照(某enable_env=True参数在同一路径下确实被 env 回填),避免用例因整条路径未触发而假通过。
- 建议:补直接断言
- 非法模块名用例只断言 SystemExit,参数改名或拼错也会假通过 @
rtp_llm/server/server_args/test/server_args_test.py:618- 建议:改为直接对
parse_external_model_packages做单元断言,用assertRaisesRegex(argparse.ArgumentTypeError, "invalid external model package path")固定错误信息;若必须走整条 CLI,可用contextlib.redirect_stderr断言 stderr 含该提示并断言exc.code == 2。建议同时参数化补齐../x、a/b.py、a-b、前导点等典型非法输入。
- 建议:改为直接对
- fp8 字节期望隐式依赖 ENABLE_FP8/USING_ROCM 构建宏,而新 cc_test 无任何平台门禁 @
rtp_llm/cpp/cache/test/BUILD:88- 建议:两种做法任选:一是给新 target 加
target_compatible_with或按 configselect,仅在定义了ENABLE_FP8/USING_ROCM的配置下启用;二是让断言不依赖编译期 dtype 大小——优先断言block_size()(元素数,与平台无关),需要字节数时用block_size() * getTypeSize(dtype)推导而非硬编码 656。最低成本兜底是在断言前加ASSERT_EQ(getTypeSize(DataType::TYPE_FP8_E4M3), 1u)前置校验,使非 GPU 构建下失败原因自解释。建议在 cpu/arm 配置各跑一次该 target 确认。另请补齐边界:is_fp8判定含TYPE_FP8_E8M0(MLAKVCacheSpec.h:40,该类型getTypeSize恒为 1)但无用例,seq_size_per_block恒为 1、== 0 → 1兜底(:33)也未验证。
- 建议:两种做法任选:一是给新 target 加
- 平台分支测试复制了生产代码同一个 #if,无法发现门控写错且 ROCm 紧凑布局零执行覆盖 @
rtp_llm/cpp/cache/test/MLAKVCacheSpecTest.cc:63- 建议:把「是否使用紧凑布局」从预处理宏提升为可注入参数(如
SpecBuildContext增加显式字段,或抽出纯函数mlaElemsPerToken(dtype, no_pe, rope, use_compact)),使两条布局在任意平台都能被枚举测试,测试也不再需要复制#if;期望值由测试独立给出的常量表提供(按 spec 类型 + dtype + sparse 三元组)。另请确认 ROCm 流水线确实会执行//rtp_llm/cpp/cache/test:mla_kv_cache_spec_test(该包多数 target 依赖 CUDA,通配符跑法在 ROCm 上可能整包失败);若不会,可参考rtp_llm/utils/test/BUILD:96-107的tags = ["rocm"]+exec_properties约定单独挂一个 ROCm target。
- 建议:把「是否使用紧凑布局」从预处理宏提升为可注入参数(如
- MLA KV cache 布局改动与本 PR 声明的插件支持目标无功能耦合,且缺少设计说明 @
rtp_llm/cpp/cache/MLAKVCacheSpec.h:47- 建议:建议把 KV cache 布局改动拆成独立 PR,并在 description 中给出:目标 ROCm 内核与其期望的每 token 字节布局、为什么用
is_sparse而非独立布局开关判定、fp8 对齐校验的动机(哪个配置触发了静默截断)、以及 ROCm 侧 smoke 结果。若因排期必须同 PR 提交,请至少拆成两个独立 commit(一个 plugin、一个 kv cache),便于后续 bisect 与回滚。
- 建议:建议把 KV cache 布局改动拆成独立 PR,并在 description 中给出:目标 ROCm 内核与其期望的每 token 字节布局、为什么用
P3
- create_propose_model_config 重建 ModelArgs 时漏拷 external_model_packages @
rtp_llm/model_factory.py:447- 建议:在 :453 之后补一行
propose_model_args.external_model_packages = model_args.external_model_packages,让新字段与其余字段一致地传播;或在该方法开头也调用一次load_external_model_packages(model_args.external_model_packages)(对已 import 的包是幂等的)。若确认要保留顺序依赖,请在该方法 docstring 中显式写明「必须在create_model_config之后调用,外部模型包由后者加载」。
- 建议:在 :453 之后补一行
- -h/--help 仍被注册为 HELP 环境变量映射,本 PR 新增的 enable_env 恰是其自然修复点 @
rtp_llm/server/server_args/server_args.py:186- 建议:先按上文修好 parser 层
enable_env的透传,再在__init__中改用add_help=False后手动以enable_env=False添加 help 动作(或在_register_env_mapping中对argparse._HelpAction/_VersionAction直接跳过),使控制类动作永不进入 env 映射表,随后即可移除generate_args_from_env_clean.py:130的特判。属顺手清理,可另开 PR。
- 建议:先按上文修好 parser 层
- 空项被静默丢弃使 None 与空列表下游语义不可区分,配置笔误无反馈 @
rtp_llm/server/server_args/model_group_args.py:7- 建议:若用户显式传入了该参数但解析结果为空列表,直接抛
argparse.ArgumentTypeError提示「未解析出任何有效模块路径」,让配置笔误在启动最前端 fail-fast;这样None(未配置)与非空列表成为唯一两种合法状态,也简化下游判断。
- 建议:若用户显式传入了该参数但解析结果为空列表,直接抛
- mock.patch 打在全局 importlib 模块属性上,可能把 Mock 结果写进进程级 lru_cache @
rtp_llm/utils/test/import_util_test.py:25- 建议:优选做法是让
load_external_model_packages接受可注入的 importer(默认importlib.import_module),测试传入 fake,彻底避免改写全局模块属性;若维持现状,建议在tearDown中显式调用import_optional_internal_source_entrypoint.cache_clear()与has_internal_source.cache_clear(),并加一行注释说明该 patch 的作用域是进程级。
- 建议:优选做法是让
- 未固定 dense fp8 下 k/v 分块字节与总字节不相等这一约束 @
rtp_llm/cpp/cache/test/MLAKVCacheSpecTest.cc:66- 建议:在
DenseFp8UsesNativeLayout中显式断言当前预期(例如断言k+v == 576且< block_size_bytes()),并加一行注释说明 fp8 scale 区不计入 k/v 分块、该等式仅在紧凑布局下成立。若后续 MLA fp8 会以 spec 字节进入 CP 切分路径,应作为独立的正确性问题上报。
- 建议:在
- 新测试重复实现了已有的 MLA spec 构造工具函数 @
rtp_llm/cpp/cache/test/MLAKVCacheSpecTest.cc:10- 建议:给
makeResolvedMlaSpec增加默认为 false 的is_sparse参数,新测试改为依赖:cache_config_test_utils复用该 helper;seq_size_per_block也能顺带参数化,便于补齐前述seq_size_per_block > 1覆盖。
- 建议:给
- 新增 C++ 测试文件未过 clang-format、缺文件末尾换行,且直接 include 了未声明的依赖头 @
rtp_llm/cpp/cache/test/MLAKVCacheSpecTest.cc:34- 建议:对该文件执行一次
pre-commit run clang-format --files rtp_llm/cpp/cache/test/MLAKVCacheSpecTest.cc修正=对齐并补上文件末尾换行;在test/BUILD:91的 deps 中显式加上//rtp_llm/cpp/utils:core_utils。顺带建议把MLAKVCacheSpec.h:52-54中挤在同一行的desc.tag.c_str(), no_pe改为每行一个实参,与同函数内既有三处检查保持一致。
- 建议:对该文件执行一次
- 用例名 test_cli_space_separated_value 与其实际覆盖的输入形态不一致 @
rtp_llm/server/server_args/test/server_args_test.py:560- 建议:改名为能反映 argv 形态的名字(例如
test_cli_separate_token_form_parses_comma_separated_packages),与 equals 形态用例形成「书写形式」维度的对照;并把「去重 / 去空白」的断言从 equals 形态用例中拆出为独立用例,使每个用例只声明一件事。
- 建议:改名为能反映 argv 形态的名字(例如
Checklist Findings (18 fail / 48 total)
General Principles Checklist
- [6.1] Architecture — 依赖方向:无循环依赖/跨层惊喜 → issue
新增 C++ 测试文件未过 clang-format、缺文件末尾换行,且直接 include 了未声明的依赖头
.clang-format配置AlignConsecutiveAssignments: true,.pre-commit-config.yaml对该目录启用 clang-format(仅排除rtp_llm/cpp/cutlass/)。最长 LHS 为StaticConfig::user_ft_core_dump_on_exception,故 :35 与 :38 的=前应为单空格,实际各多一个空格;:38 作为独立作用域内的单条语句本不应被补齐对齐。diff 末行显示\ No newline at end of file。此外 :5 直接#include "rtp_llm/cpp/utils/Exception.h",但test/BUILD:91-96只声明了kv_cache_specs与static_config,该头属//rtp_llm/cpp/utils:core_utils,目前靠依赖传递生效,一旦启用layering_check即断。 - [6.1] Architecture — 兼容性:外部 HTTP/RPC API、持久数据、配置、环境迁移安全 → issue
kv_lora_rank 128 对齐硬校验对全平台生效,未对齐配置由静默过量分配变为启动失败且无回退开关
use_compact_fp8_layout仅在#if USING_ROCM(.bazelrc:353,仅build:rocm)内可能为真,故 CUDA/CPU/ARM 下no_pe % 128 == 0对所有 fp8 MLA 生效,经myAssert抛RTPException导致 cache config 创建失败即服务启动失败。kv_lora_rank来自 HFconfig.json(deepseek_v2.py:622-624、kimi_k25.py:115)而非仓内硬编码,模型侧可为任意值;仓内也存在 scale 分组非 128 的 fp8 MLA 变体(按 64 元素/tile 分组),说明 128 并非唯一合法分组。改前未对齐配置按 floor 除法过量分配仍可启动,改后直接拒绝。已确认仓内经本 spec 的 MLA 模型kv_lora_rank均为 512,DSV4 在deepseek_v4.py:617/621置use_mla=False、kv_lora_rank=0并走 Op - [6.1] Architecture — 可观测性:日志/指标/超时可操作、非噪声 → issue
ROCm 稀疏 fp8 紧凑布局的 576 字节/token 与仓内 fp8 MLA 布局不吻合,且当前无任何读写方可验证
MLAKVCacheSpec.h:46-58:ROCm 下 fp8 且is_sparse改走no_pe + rope。getTypeSize(TYPE_FP8_E4M3)在 ROCm 为 1(Types.cc:110-113),故 512/64 配置得 576 字节/token,等价于 rope 也按 1 字节存且完全不预留 nope 反量化 scale 区——MLAKVCacheSpec未重写scale_block_size_bytes(),该 scale 区原本就靠no_pe/128*4这一内联项承载,移除即无处存放,没有 scale 无法做 fp8 反量化。仓内 fp8 MLA 布局为 656 字节:test_dpsk32_fp8.py:70-73的entry_size = kv_lora_rank + 4*4 + 2*qk_rope_head_dim,flash_mla_layout_probe_test.py:31的ENTRY_BYTES_V32 = 512 + 16 + 128。同时 `models_p - [6.1] Architecture — 回滚路径:风险行为存在运维回滚手段 → issue
-h/--help 仍被注册为 HELP 环境变量映射,本 PR 新增的 enable_env 恰是其自然修复点
EnvArgumentParser.__init__先设env_prefix(:181)再调super().__init__()(:186),而 argparse 在add_help=True时会调用被重写的self.add_argument("-h", "--help", ...),经上文已确认的委托链使_env_mappings["help"] = "HELP"被注册。纯 env 分支(:276-298)遍历_env_mappings,若环境中存在HELP会拼出--help <value>,导致服务器打印帮助并退出,表现为「启动即退出、无错误日志」。旁证:generate_args_from_env_clean.py:130用long_option == "--help"硬编码绕开,说明这是已知需 workaround 的历史瑕疵,非本 PR 引入。 - [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue
未固定 dense fp8 下 k/v 分块字节与总字节不相等这一约束
测试仅在 ROCm 分支断言k_block_size_bytes() + v_block_size_bytes() == block_size_bytes()(:66-68)。dense fp8 路径下该等式并不成立:block_size_bytes()为 656,而k+v仍为 512+64=576(MLAKVCacheSpec.h:55只改了elems_per_token,k_block_size/v_block_size沿用 nope/rope)。KVCacheSpecBase.h:39的splitKVPartitionBytes又以RTP_LLM_CHECK_WITH_INFO硬校验k+v == full(已确认MemoryLayoutStrategy.cc:247传入的是kv_block_stride_bytes与其折半值,非 spec 的 k/v 字节,故当前不触发)。测试把该不变式只在一个平台上断言,读者无法判断 dense fp8 的不相等是有意设计还是遗漏。 - [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue
空项被静默丢弃使 None 与空列表下游语义不可区分,配置笔误无反馈
parse_external_model_packages对空段执行continue(:11-12),因此--external_model_packages ",,"返回[]而不报错(server_args_test.py:611已将该行为固化为期望)。[]非 None,会被_apply_config_bindings(server_args.py:413的value is not None)真实写入model_args,但在import_util.py:30的if not package_names: return处与默认None因 falsy 短路而完全等价,字段声明的Optional[List[str]]因此不承载任何可区分信息。运维若因模板拼接错误传入全空值,会得到与未配置完全相同的「成功启动但插件未加载」结果,随后在model_type阶段才失败,排查成本高。 - [6.1] Quality — Commit 原子、message 与行为匹配 → issue
MLA KV cache 布局改动与本 PR 声明的插件支持目标无功能耦合,且缺少设计说明
本 PR 12 个改动文件中有 9 个围绕external_model_packages这一条主线(分支名亦标明本 PR 目标为 plugin support),而MLAKVCacheSpec.h+MLAKVCacheSpecTest.cc+cpp/cache/test/BUILD改的是 MLA fp8 KV cache 字节布局与启动期硬校验,与插件加载无任何调用或依赖关系(import_util.py与cpp/cache/之间无引用)。PR description 未给出该布局改动的动机、目标 ROCm 内核,以及 576 字节/token 的依据;代码中也无注释解释为何「注意力稀疏性」(is_sparse)可以决定「latent KV 的存储宽度」这两个本不相关的概念。二者混在一个 PR 中,CI 失败时无法按功能二分定位,KV cache 这一高风险改动也被插件功能的描述掩盖。 - [6.1] Quality — Mega-PR 已拆分为独立变更 → issue
MLA KV cache 布局改动与本 PR 声明的插件支持目标无功能耦合,且缺少设计说明
本 PR 12 个改动文件中有 9 个围绕external_model_packages这一条主线(分支名亦标明本 PR 目标为 plugin support),而MLAKVCacheSpec.h+MLAKVCacheSpecTest.cc+cpp/cache/test/BUILD改的是 MLA fp8 KV cache 字节布局与启动期硬校验,与插件加载无任何调用或依赖关系(import_util.py与cpp/cache/之间无引用)。PR description 未给出该布局改动的动机、目标 ROCm 内核,以及 576 字节/token 的依据;代码中也无注释解释为何「注意力稀疏性」(is_sparse)可以决定「latent KV 的存储宽度」这两个本不相关的概念。二者混在一个 PR 中,CI 失败时无法按功能二分定位,KV cache 这一高风险改动也被插件功能的描述掩盖。 - [6.1] Quality — PR description 说明动机与设计 → issue
MLA KV cache 布局改动与本 PR 声明的插件支持目标无功能耦合,且缺少设计说明
本 PR 12 个改动文件中有 9 个围绕external_model_packages这一条主线(分支名亦标明本 PR 目标为 plugin support),而MLAKVCacheSpec.h+MLAKVCacheSpecTest.cc+cpp/cache/test/BUILD改的是 MLA fp8 KV cache 字节布局与启动期硬校验,与插件加载无任何调用或依赖关系(import_util.py与cpp/cache/之间无引用)。PR description 未给出该布局改动的动机、目标 ROCm 内核,以及 576 字节/token 的依据;代码中也无注释解释为何「注意力稀疏性」(is_sparse)可以决定「latent KV 的存储宽度」这两个本不相关的概念。二者混在一个 PR 中,CI 失败时无法按功能二分定位,KV cache 这一高风险改动也被插件功能的描述掩盖。 - [6.1] Quality — 逻辑变更未混入无关格式化 → issue
新增 C++ 测试文件未过 clang-format、缺文件末尾换行,且直接 include 了未声明的依赖头
.clang-format配置AlignConsecutiveAssignments: true,.pre-commit-config.yaml对该目录启用 clang-format(仅排除rtp_llm/cpp/cutlass/)。最长 LHS 为StaticConfig::user_ft_core_dump_on_exception,故 :35 与 :38 的=前应为单空格,实际各多一个空格;:38 作为独立作用域内的单条语句本不应被补齐对齐。diff 末行显示\ No newline at end of file。此外 :5 直接#include "rtp_llm/cpp/utils/Exception.h",但test/BUILD:91-96只声明了kv_cache_specs与static_config,该头属//rtp_llm/cpp/utils:core_utils,目前靠依赖传递生效,一旦启用layering_check即断。 - [6.1] Software Engineering — DRY:重复非平凡逻辑被抽取或显式复用 → issue
新测试重复实现了已有的 MLA spec 构造工具函数
cpp/cache/test/CacheConfigTestUtils.h:116已有makeResolvedMlaSpec(dtype, kv_lora_rank, rope_head_dim, seq_size_per_block, tag),逻辑与新增的makeMlaSpec几乎一致(同样填AttentionConfigs、KVCacheSpecDesc、SpecBuildContext后dynamic_pointer_cast),差别仅在新函数额外设置attn.is_sparse且把rope_head_dim/seq_size_per_block硬编码为 64/1。该 helper 已通过visibility = ["//visibility:public"]的cache_config_test_utilscc_library 对外暴露(test/BUILD:50-61),本次却另起一份,后续SpecBuildContext字段变更需改两处。 - [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue
平台分支测试复制了生产代码同一个 #if,无法发现门控写错且 ROCm 紧凑布局零执行覆盖
MLAKVCacheSpecTest.cc:63-72用与生产代码MLAKVCacheSpec.h:47完全相同的#if USING_ROCM复制平台判断,因此测试只复述「被编译进来的那一个分支」的算术结果;若平台门控条件本身写错,实现与测试会同步走错分支,测试永远无法暴露。在默认 CUDA CI 下 ROCm 分支既不编译也不执行,本次新增的紧凑布局(576 字节)实际零执行覆盖,#else期望值退化为与DenseFp8UsesNativeLayout完全相同的 656。边界组合也缺:sparse + fp8 + 未对齐 rank(ROCm 应放行、CUDA 应抛错)无用例。 - [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue
用例名 test_cli_space_separated_value 与其实际覆盖的输入形态不一致
该用例名为 space separated,但传入值"atom.plugin.rtpllm.models,plugin.extra"是逗号分隔,且parse_external_model_packages(model_group_args.py:9)只按,切分——空格分隔的"a b"会被isidentifier()判定为非法并拒绝。它真正区别于test_cli_equals_value_deduplicates_packages的地方是「--flag value独立 argv token 形态」而非--flag=value形态。名称误导会让后续维护者误以为存在空格分隔语法,也掩盖了这两个用例实际在校验的 argv 形态差异。 - [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue
未固定 dense fp8 下 k/v 分块字节与总字节不相等这一约束
测试仅在 ROCm 分支断言k_block_size_bytes() + v_block_size_bytes() == block_size_bytes()(:66-68)。dense fp8 路径下该等式并不成立:block_size_bytes()为 656,而k+v仍为 512+64=576(MLAKVCacheSpec.h:55只改了elems_per_token,k_block_size/v_block_size沿用 nope/rope)。KVCacheSpecBase.h:39的splitKVPartitionBytes又以RTP_LLM_CHECK_WITH_INFO硬校验k+v == full(已确认MemoryLayoutStrategy.cc:247传入的是kv_block_stride_bytes与其折半值,非 spec 的 k/v 字节,故当前不触发)。测试把该不变式只在一个平台上断言,读者无法判断 dense fp8 的不相等是有意设计还是遗漏。
RTP-LLM Checklist
- [I] 代码质量 — 同一功能用统一工具函数 → issue
新测试重复实现了已有的 MLA spec 构造工具函数
cpp/cache/test/CacheConfigTestUtils.h:116已有makeResolvedMlaSpec(dtype, kv_lora_rank, rope_head_dim, seq_size_per_block, tag),逻辑与新增的makeMlaSpec几乎一致(同样填AttentionConfigs、KVCacheSpecDesc、SpecBuildContext后dynamic_pointer_cast),差别仅在新函数额外设置attn.is_sparse且把rope_head_dim/seq_size_per_block硬编码为 64/1。该 helper 已通过visibility = ["//visibility:public"]的cache_config_test_utilscc_library 对外暴露(test/BUILD:50-61),本次却另起一份,后续SpecBuildContext字段变更需改两处。
Python Static-First Checklist
- [P.G] 测试规范 — mock.patch target 是使用处而非定义处 → issue
mock.patch 打在全局 importlib 模块属性上,可能把 Mock 结果写进进程级 lru_cache
import_util.py:1是import importlib后在 :37 直接调用importlib.import_module,因此 patch targetrtp_llm.utils.import_util.importlib.import_module实际改写的是全局importlib模块属性,而非使用处的局部名字。同一模块内import_optional_internal_source_entrypoint(@lru_cache(maxsize=None),:48-67)与LazyModuleRegistry.import_module都调用同一函数:若 patch 窗口内被间接触发,前者会拿到 Mock 并把True永久缓存进本进程,污染同一 py_test 内后续测试。当前文件无触发路径,属潜在风险。 - [P.G] 测试规范 — mock/fake/stub 不得替代本次声称覆盖的生产边界 → issue
外部包加载的两个生产接入点零测试覆盖,单测把 import 边界整体 mock
全仓load_external_model_packages仅两个生产调用点:server_config_setup.py:398(在_infer_model_type之前)与model_factory.py:342(在get_model_cls之前),两者均无测试。新增单测三个用例全部mock.patch(...importlib.import_module),只断言调用次序与异常包装,从未真实 import 过一个包,也未断言「import 后模型确实注册成功」。而该特性的功能本质正是「靠 import 副作用完成register_model并写入_hf_architecture_2_ft」以及「加载必须早于模型类型解析」,这两点当前零回归保护。CLI 侧用例也无法补位:setup_args()(server_args.py:531)并不调用setup_default_args,故不触发加载。若有人把加载点移到类型推断之后,全部测试仍绿。 - [P.G] 测试规范 — pytest.raises 带 match 参数 → issue
非法模块名用例只断言 SystemExit,参数改名或拼错也会假通过
test_invalid_module_path_is_rejected只有with self.assertRaises(SystemExit)。argparse 对「未识别的参数」「缺少取值」等任何错误都以SystemExit(2)退出,因此该用例无法区分失败原因:一旦--external_model_packages被改名或拼错,用例依然通过,而model_group_args.py:13中part.isidentifier()这条真正要固定的安全校验契约就失去保护——这正是阻断路径穿越写法的唯一防线。
Strengths
- 安全边界选择正确且工具链自洽:该特性本质是「按名字 import 任意模块并执行其 import-time 代码」,等价于任意代码执行;
enable_env=False把配置面从环境变量(易被间接注入的弱信任通道)收敛为部署方可控的命令行。generate_args_from_env_clean.py:33按action.dest in env_mappings过滤,也不会把该 flag 塞回来,不存在「禁用 env 但工具链绕过」的漏洞。 - 输入校验下沉到 argparse
type层且足够严格:parse_external_model_packages(model_group_args.py:7)对每个点分段做isidentifier(),天然拒绝相对导入、路径分隔符与连字符,同时完成去空白与保序去重,脏值不进入运行期。 - 错误语义显式且 fail-fast:非法路径抛
ArgumentTypeError由 argparse 收敛为SystemExit(2);导入失败在import_util.py:39以raise RuntimeError(...) from error中止启动并附带sys.path,未静默降级为二段式的「模型类型未注册」。 - 加载时机与注册语义吻合:
server_config_setup.py:398早于_infer_model_type(:400-403),使外部包经register_model(support_architectures=)写入_hf_architecture_2_ft的架构名能参与自动推断,无需强制显式传--model_type。 - 跨进程传播完整:
model_factory.py:342在create_model_config内统一重新加载,覆盖 spawn 出的 backend / frontend / vit / dash_sc 等入口,不依赖父进程sys.modules;新字段为纯List[str],随配置对象 pickle 跨 spawn 边界不引入对外部包类的反序列化依赖。 - 向后兼容零风险:
enable_env默认True,约两百个既有参数注册与 env 行为完全不变;ModelArgs仅追加 slot 且同步维护__init__默认值(model_args.py:30/:60),属加法式局部扩展。 MLAKVCacheSpec.h:52把此前隐藏在no_pe / 128 * 4整数除法里的「必须按 128 对齐」隐式前提显式化——改前 rank 不足 128 会把 scale 空间静默算成 0 导致 KV block 少分配,现改为启动期 fail-fast,错误信息带 tag 与实际值。- 新增
MLAKVCacheSpecTest的SetUp/TearDown(:33-39)正确保存并恢复StaticConfig::user_ft_core_dump_on_exception,这是EXPECT_THROW生效的必要前提(该开关为 true 时myAssert直接 abort),与同目录既有测试约定一致且不污染全局状态;Bf16LayoutDoesNotDependOnSparseMode锁定了「非 fp8 路径不受is_sparse影响」这一不变量。 - 新增
mla_kv_cache_spec_test只依赖kv_cache_specs+static_config+torch_deps(),未复用带 CUDA 依赖的test_deps也未占用 GPU,与同包纯布局测试一致。 test_import_failure_is_fail_fast_and_keeps_context(import_util_test.py:28-36)同时断言异常类型、信息正则、__cause__与assert_called_once_with,把「首个包失败即中断、不吞异常、保留原始异常」三个契约一次钉住。
| action = self._optionals.add_argument(*args, **kwargs) | ||
|
|
||
| self._register_env_mapping(action, args, env_name) | ||
| if enable_env: |
There was a problem hiding this comment.
[P2] enable_env 在两个同名入口语义分叉,EnvArgumentParser 层因未向委托 group 透传而静默失效
argparse.ArgumentParser.__init__ 通过 self.add_argument_group(...) 创建 _positionals/_optionals,而该方法被重写(:204-211)返回 EnvArgumentGroup,故 :225/:227 的 self._optionals.add_argument(*args, **kwargs) 进入 EnvArgumentGroup.add_argument,其 enable_env 取默认 True 并在 :169-170 无条件注册;enable_env/env_name 是外层 keyword-only 形参、不在 **kwargs 中,不会下传。因此 :229 的 if enable_env: 只是重复写同一映射,无法移除已注册项。旁证:-h/--help 经 parser 层注册后确实进入 _env_mappings,所以 generate_args_from_env_clean.py:130 才需硬编码过滤 --help...
建议: 将 enable_env 与 env_name 显式下传给委托调用(self._optionals.add_argument(*args, enable_env=enable_env, env_name=env_name, **kwargs))并去掉外层重复的 _register_env_mapping;或直接删除 EnvArgumentParser.add_argument 上这个当前无业务调用方的参数(YAGNI),只保留 EnvArgumentGroup 一个可信入口。若保留,请补一条单测:直接在 parser 上注册 enable_env=False 参数,断言其 dest 不出现在 get_env_mappings() 中。
| ) | ||
| model_group.add_argument( | ||
| "--external_model_packages", | ||
| enable_env=False, |
There was a problem hiding this comment.
[P2] 仅 CLI 通道对运维完全不可见——无任何告警,env→CLI 生成脚本永不产出该参数,最终报错误导根因
enable_env=False 使该 dest 永不进入 _env_mappings:parse_args 两条 env 回填分支(server_args.py:276、:350)都读不到它,:385 的 [EnvMapping] 调试日志也排除它,generate_args_from_env_clean.py:33 的 action.dest in env_mappings 过滤同样使 env→CLI 生成脚本永不输出该 flag。同组其余参数全部带 env_name,server_config_setup.py:406 的报错文案本身还在引导用户使用 MODEL_TYPE 环境变量。运维按此惯例设置 EXTERNAL_MODEL_PACKAGES 时值被无声丢弃,最终暴露为 model_type is not set and could not be inferred,与真实根因完全脱节。server_args_test.py:603 还把这种静默固化为断言;help 文案只写「仅支持命令行配置」,未说明动机与信任边界。
建议: 在 parse_args 完成后对声明 enable_env=False 的 dest 做显式检查:若 EXTERNAL_MODEL_PACKAGES / RTP_LLM_EXTERNAL_MODEL_PACKAGES 存在于 os.environ 而 CLI 未传,输出 logging.warning 明确提示「该参数出于安全考虑仅支持命令行配置,环境变量已被忽略」,并在 test_environment_variables_are_ignored 中用 assertLogs 断言该告警,把「静默」变为「可观测的拒绝」。help 文案补一句该参数会在启动时 import 并执行指定包的代码。同时在 PR description 或部署文档中说明:纯 env 部署下如何在启动命令追加该参数、回滚手段(去掉该 flag 即完全不生效),并确认 load_external_model_packages 的 INFO 日志在各角色进程都会输出。
| self._register_env_mapping(action, args, env_name) | ||
| return action | ||
|
|
||
| def _register_env_mapping( |
There was a problem hiding this comment.
📍 实际位置 rtp_llm/server/server_args/server_args.py:268(不在 diff 展示范围内,就近挂载)
[P2] 采用该仅 CLI 参数会翻转 has_cmd_args 分支,使其他所有 env 参数的畸形值从 fail-fast 退化为静默取默认值
has_cmd_args = args is not None or (len(sys.argv) > 1) 把解析分成两种模式,两者对畸形 env 值并不等价:纯 env 分支(:272-298)把 env 拼成 argv 交给 argparse,int("abc") 失败即经 self.error → SystemExit(2);有 CLI 参数的回填分支虽对 ArgumentTypeError 调 self.error(:371-372),但紧随的 except (ValueError, TypeError): pass(:373-375)会静默丢弃并保留默认值。由于本参数只能从命令行传入,原本完全无 argv 的纯 env 部署一旦启用该特性就被迫引入首个 CLI 参数并整体切到回填分支,于是 MAX_SEQ_LEN=abc(model_group_args.py:105 为 type=int)这类配置从「启动即失败」变为「静默取默认值继续跑」。该分支不对称虽为既有代码,但本 PR 新增了让生产部署普遍触达它的理由。
建议: 把 :373 的 except (ValueError, TypeError): pass 与 :371 的 ArgumentTypeError 分支统一为 self.error(...),让两种解析模式对畸形 env 值都 fail-fast;若为兼容性需保留,至少补 logging.warning 记录被丢弃的 env_name 与原值。并补一个混合场景测试:sys.argv 只带 --external_model_packages,同时用 env 设 MODEL_TYPE 与一个畸形 MAX_SEQ_LEN,断言 env 项正确绑定且畸形值不被静默吞掉。
| self._parser._register_env_mapping(action, args, env_name) | ||
| return action | ||
|
|
||
| def __getattr__(self, name): |
There was a problem hiding this comment.
📍 实际位置 rtp_llm/server/server_args/server_args.py:178(不在 diff 展示范围内,就近挂载)
[P2] 仅 CLI 保证依赖类级 _env_mappings 的「缺失」而非显式拒绝
_env_mappings 是 EnvArgumentParser 的类属性(:178),由 _register_env_mapping 以 EnvArgumentParser._env_mappings[action.dest] = ...(:257)写入,进程内所有实例共享且从不清空;get_env_mappings 返回该类字典的拷贝。enable_env=False 只是「不写入」,未记录任何拒绝意图。因此只要任何代码路径以相同 dest 注册一次带 env 的参数(新增别名、或某次重构误删 enable_env=False),这条刻意收敛的安全边界就会静默恢复,且现有测试断言的是结果(值为 None)而非机制(dest 不在映射表中),无法拦住这种回归。新测试类 _setup(:550-553)也未像同文件 ServerArgsGrammarConfigTest(:646-650)那样 importlib.reload,其「env 被忽略」断言实际依赖全局字典恰好未被污染。
建议: 在 EnvArgumentParser 上新增 _env_disabled_dests: Set[str],enable_env=False 时写入该集合;_register_env_mapping 遇到已在集合中的 dest 直接跳过并告警,:276 与 :350 两处 env 读取回路也跳过该集合。这样「仅 CLI」从依赖遗漏变为正向拒绝,不再依赖全局状态的洁净程度。并补一条断言 assertNotIn("external_model_packages", parser.get_env_mappings()) 的单测,对机制而非结果建立护栏。
|
|
||
| bool use_compact_fp8_layout = false; | ||
| #if USING_ROCM | ||
| use_compact_fp8_layout = is_fp8 && attn.is_sparse; |
There was a problem hiding this comment.
[P2] ROCm 稀疏 fp8 紧凑布局的 576 字节/token 与仓内 fp8 MLA 布局不吻合,且当前无任何读写方可验证
MLAKVCacheSpec.h:46-58:ROCm 下 fp8 且 is_sparse 改走 no_pe + rope。getTypeSize(TYPE_FP8_E4M3) 在 ROCm 为 1(Types.cc:110-113),故 512/64 配置得 576 字节/token,等价于 rope 也按 1 字节存且完全不预留 nope 反量化 scale 区——MLAKVCacheSpec 未重写 scale_block_size_bytes(),该 scale 区原本就靠 no_pe/128*4 这一内联项承载,移除即无处存放,没有 scale 无法做 fp8 反量化。仓内 fp8 MLA 布局为 656 字节:test_dpsk32_fp8.py:70-73 的 entry_size = kv_lora_rank + 4*4 + 2*qk_rope_head_dim,flash_mla_layout_probe_test.py:31 的 ENTRY_BYTES_V32 = 512 + 16 + 128。同时 `model...
建议: 合并前请补充证据:明确目标 ROCm 内核确实按 [512B fp8 nope | 64B fp8 rope] 读写且不需要 scale 区。若 rope 实际仍为 bf16,应写成 no_pe + rope * 2;若需要 scale 区应显式计算而非复用非 fp8 分支的表达式。注意 spec->block_size_bytes() 会直接成为 kv_block_stride_bytes,而 DeepSeek V3.2(deepseek_v2.py:711 设 is_sparse = True,kv_lora_rank=512)在 ROCm 构建下已可触达该分支,一旦有 kernel 沿用 656 字节假设即为越界写。若 ROCm MLA 尚未落地,建议把该分支拆出本 PR、与 ROCm sparse MLA 读写实现一并提交;若保留,请在 MLAKVCacheSpec.h:46 上方注释说明「为何不需要 scale 区、rope 为何按 1 字节存」,并在 build() 成功后按 tag 打一条 INFO 记录最终布局与 elems_per_token。
Checklist: [6.1] 可观测性:日志/指标/超时可操作、非噪声
| import_module.assert_not_called() | ||
|
|
||
| @mock.patch( | ||
| "rtp_llm.utils.import_util.importlib.import_module", |
There was a problem hiding this comment.
[P3] mock.patch 打在全局 importlib 模块属性上,可能把 Mock 结果写进进程级 lru_cache
import_util.py:1 是 import importlib 后在 :37 直接调用 importlib.import_module,因此 patch target rtp_llm.utils.import_util.importlib.import_module 实际改写的是全局 importlib 模块属性,而非使用处的局部名字。同一模块内 import_optional_internal_source_entrypoint(@lru_cache(maxsize=None),:48-67)与 LazyModuleRegistry.import_module 都调用同一函数:若 patch 窗口内被间接触发,前者会拿到 Mock 并把 True 永久缓存进本进程,污染同一 py_test 内后续测试。当前文件无触发路径,属潜在风险。
建议: 优选做法是让 load_external_model_packages 接受可注入的 importer(默认 importlib.import_module),测试传入 fake,彻底避免改写全局模块属性;若维持现状,建议在 tearDown 中显式调用 import_optional_internal_source_entrypoint.cache_clear() 与 has_internal_source.cache_clear(),并加一行注释说明该 patch 的作用域是进程级。
Checklist: [P.G] mock.patch target 是使用处而非定义处
| #if USING_ROCM | ||
| constexpr size_t expected_bytes = 512 + 64; | ||
| EXPECT_EQ(spec->block_size_bytes(), expected_bytes); | ||
| EXPECT_EQ( |
There was a problem hiding this comment.
[P3] 未固定 dense fp8 下 k/v 分块字节与总字节不相等这一约束
测试仅在 ROCm 分支断言 k_block_size_bytes() + v_block_size_bytes() == block_size_bytes()(:66-68)。dense fp8 路径下该等式并不成立:block_size_bytes() 为 656,而 k+v 仍为 512+64=576(MLAKVCacheSpec.h:55 只改了 elems_per_token,k_block_size/v_block_size 沿用 nope/rope)。KVCacheSpecBase.h:39 的 splitKVPartitionBytes 又以 RTP_LLM_CHECK_WITH_INFO 硬校验 k+v == full(已确认 MemoryLayoutStrategy.cc:247 传入的是 kv_block_stride_bytes 与其折半值,非 spec 的 k/v 字节,故当前不触发)。测试把该不变式只在一个平台上断言,读者无法判断 dense fp8 的不相等是有意设计还是遗漏。
建议: 在 DenseFp8UsesNativeLayout 中显式断言当前预期(例如断言 k+v == 576 且 < block_size_bytes()),并加一行注释说明 fp8 scale 区不计入 k/v 分块、该等式仅在紧凑布局下成立。若后续 MLA fp8 会以 spec 字节进入 CP 切分路径,应作为独立的正确性问题上报。
Checklist: [6.1] 状态不变量:创建/更新/失败/重试/回滚路径有效;[6.1] 边界 case 覆盖(空、单元素、最大值)
| namespace rtp_llm::test { | ||
| namespace { | ||
|
|
||
| std::shared_ptr<MLAKVCacheSpec> |
There was a problem hiding this comment.
[P3] 新测试重复实现了已有的 MLA spec 构造工具函数
cpp/cache/test/CacheConfigTestUtils.h:116 已有 makeResolvedMlaSpec(dtype, kv_lora_rank, rope_head_dim, seq_size_per_block, tag),逻辑与新增的 makeMlaSpec 几乎一致(同样填 AttentionConfigs、KVCacheSpecDesc、SpecBuildContext 后 dynamic_pointer_cast),差别仅在新函数额外设置 attn.is_sparse 且把 rope_head_dim/seq_size_per_block 硬编码为 64/1。该 helper 已通过 visibility = ["//visibility:public"] 的 cache_config_test_utils cc_library 对外暴露(test/BUILD:50-61),本次却另起一份,后续 SpecBuildContext 字段变更需改两处。
建议: 给 makeResolvedMlaSpec 增加默认为 false 的 is_sparse 参数,新测试改为依赖 :cache_config_test_utils 复用该 helper;seq_size_per_block 也能顺带参数化,便于补齐前述 seq_size_per_block > 1 覆盖。
Checklist: [6.1] DRY:重复非平凡逻辑被抽取或显式复用;[I] 同一功能用统一工具函数
| class MLAKVCacheSpecTest: public ::testing::Test { | ||
| protected: | ||
| void SetUp() override { | ||
| old_core_dump_on_exception_ = StaticConfig::user_ft_core_dump_on_exception; |
There was a problem hiding this comment.
[P3] 新增 C++ 测试文件未过 clang-format、缺文件末尾换行,且直接 include 了未声明的依赖头
.clang-format 配置 AlignConsecutiveAssignments: true,.pre-commit-config.yaml 对该目录启用 clang-format(仅排除 rtp_llm/cpp/cutlass/)。最长 LHS 为 StaticConfig::user_ft_core_dump_on_exception,故 :35 与 :38 的 = 前应为单空格,实际各多一个空格;:38 作为独立作用域内的单条语句本不应被补齐对齐。diff 末行显示 \ No newline at end of file。此外 :5 直接 #include "rtp_llm/cpp/utils/Exception.h",但 test/BUILD:91-96 只声明了 kv_cache_specs 与 static_config,该头属 //rtp_llm/cpp/utils:core_utils,目前靠依赖传递生效,一旦启用 layering_check 即断。
建议: 对该文件执行一次 pre-commit run clang-format --files rtp_llm/cpp/cache/test/MLAKVCacheSpecTest.cc 修正 = 对齐并补上文件末尾换行;在 test/BUILD:91 的 deps 中显式加上 //rtp_llm/cpp/utils:core_utils。顺带建议把 MLAKVCacheSpec.h:52-54 中挤在同一行的 desc.tag.c_str(), no_pe 改为每行一个实参,与同函数内既有三处检查保持一致。
Checklist: [6.1] 依赖方向:无循环依赖/跨层惊喜;[6.1] 逻辑变更未混入无关格式化
|
|
||
| self.assertIsNone(py_env_configs.model_args.external_model_packages) | ||
|
|
||
| def test_cli_space_separated_value(self): |
There was a problem hiding this comment.
[P3] 用例名 test_cli_space_separated_value 与其实际覆盖的输入形态不一致
该用例名为 space separated,但传入值 "atom.plugin.rtpllm.models,plugin.extra" 是逗号分隔,且 parse_external_model_packages(model_group_args.py:9)只按 , 切分——空格分隔的 "a b" 会被 isidentifier() 判定为非法并拒绝。它真正区别于 test_cli_equals_value_deduplicates_packages 的地方是「--flag value 独立 argv token 形态」而非 --flag=value 形态。名称误导会让后续维护者误以为存在空格分隔语法,也掩盖了这两个用例实际在校验的 argv 形态差异。
建议: 改名为能反映 argv 形态的名字(例如 test_cli_separate_token_form_parses_comma_separated_packages),与 equals 形态用例形成「书写形式」维度的对照;并把「去重 / 去空白」的断言从 equals 形态用例中拆出为独立用例,使每个用例只声明一件事。
Checklist: [6.1] 新逻辑有聚焦单测 + 相关集成/smoke 测试
Supersedes #1266 to refresh the PR branch after root-authored commits caused the CLA check to stay pending.
Summary:
kv_lora_rank128-alignment validationValidation:
unit_testspassed: https://github.com/yuzho-amd/rtp-llm/actions/runs/32711071297/job/97382471222rocm_general_layer_norm_testpassed in that fork CI run