patch_vllm_flashinfer_b12x_swigluoai.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. #!/usr/bin/env python3
  2. """Apply the runtime subset of vLLM PR #47392 needed by FlashInfer B12x.
  3. The upstream PR also changes tests and documentation. This downstream patch
  4. only teaches the production backend to pass SwiGLU-OAI parameters through to
  5. FlashInfer and lets the NVFP4 oracle select it when that FlashInfer API exists.
  6. The patch is idempotent. It skips refs that predate FlashInfer B12x and fails
  7. on unknown source shapes instead of making a best-effort rewrite.
  8. """
  9. from __future__ import annotations
  10. import sys
  11. from pathlib import Path
  12. EXPERT_REL = Path(
  13. "vllm/model_executor/layers/fused_moe/experts/flashinfer_b12x_moe.py"
  14. )
  15. ORACLE_REL = Path("vllm/model_executor/layers/fused_moe/oracle/nvfp4.py")
  16. FLASHINFER_UTIL_REL = Path("vllm/utils/flashinfer.py")
  17. class PatchError(RuntimeError):
  18. pass
  19. def replace_once(text: str, old: str, new: str, description: str) -> str:
  20. count = text.count(old)
  21. if count != 1:
  22. raise PatchError(
  23. f"expected one {description} source anchor, found {count}; "
  24. "the vLLM source shape has changed"
  25. )
  26. return text.replace(old, new, 1)
  27. def patch_flashinfer_util(text: str) -> str:
  28. helper_name = "has_flashinfer_b12x_moe_activation"
  29. if f"def {helper_name}()" not in text:
  30. if "\nimport inspect\n" not in text:
  31. text = replace_once(
  32. text,
  33. "import importlib.util\n",
  34. "import importlib.util\nimport inspect\n",
  35. "FlashInfer import",
  36. )
  37. anchor = (
  38. " return True\n\n\n"
  39. "@functools.cache\n"
  40. "def has_nvidia_artifactory() -> bool:"
  41. )
  42. helper = (
  43. " return True\n\n\n"
  44. "@functools.cache\n"
  45. f"def {helper_name}() -> bool:\n"
  46. ' """Return whether B12xMoEWrapper accepts SwiGLU parameters."""\n'
  47. " if not has_flashinfer_b12x_moe():\n"
  48. " return False\n"
  49. ' mod = _get_submodule("flashinfer.fused_moe")\n'
  50. ' if mod is None or not hasattr(mod, "B12xMoEWrapper"):\n'
  51. " return False\n"
  52. " return (\n"
  53. ' "swiglu_limit" '
  54. "in inspect.signature(mod.B12xMoEWrapper).parameters\n"
  55. " )\n\n\n"
  56. "@functools.cache\n"
  57. "def has_nvidia_artifactory() -> bool:"
  58. )
  59. text = replace_once(text, anchor, helper, "B12x capability helper")
  60. export = f' "{helper_name}",\n'
  61. if export not in text:
  62. text = replace_once(
  63. text,
  64. ' "has_flashinfer_b12x_moe",\n',
  65. ' "has_flashinfer_b12x_moe",\n' + export,
  66. "FlashInfer __all__ entry",
  67. )
  68. if f"def {helper_name}()" not in text or export not in text:
  69. raise PatchError("FlashInfer B12x activation capability helper is incomplete")
  70. return text
  71. def patch_expert(text: str) -> str:
  72. helper_name = "has_flashinfer_b12x_moe_activation"
  73. helper_import = f" {helper_name},\n"
  74. if helper_import not in text:
  75. text = replace_once(
  76. text,
  77. " has_flashinfer_b12x_moe,\n",
  78. " has_flashinfer_b12x_moe,\n" + helper_import,
  79. "FlashInfer B12x helper import",
  80. )
  81. activation_entry = (
  82. ' MoEActivation.SWIGLUOAI_UNINTERLEAVE: '
  83. '"swigluoai_uninterleave",\n'
  84. )
  85. if activation_entry not in text:
  86. text = replace_once(
  87. text,
  88. ' MoEActivation.RELU2_NO_MUL: "relu2",\n',
  89. ' MoEActivation.RELU2_NO_MUL: "relu2",\n' + activation_entry,
  90. "B12x activation map entry",
  91. )
  92. if " self.swiglu_alpha = _resolve(" not in text:
  93. config_block = """
  94. # ModelOpt NVFP4 checkpoints carry SwiGLU parameters on moe_config,
  95. # while other quantization paths may put them on quant_config.
  96. def _resolve(
  97. quant_val: float | None, moe_val: float | None
  98. ) -> float | None:
  99. return quant_val if quant_val is not None else moe_val
  100. self.swiglu_alpha = _resolve(
  101. quant_config.gemm1_alpha, moe_config.swiglu_alpha
  102. )
  103. self.swiglu_beta = _resolve(
  104. quant_config.gemm1_beta, moe_config.swiglu_beta
  105. )
  106. self.swiglu_limit = _resolve(
  107. quant_config.gemm1_clamp_limit, moe_config.swiglu_limit
  108. )
  109. if activation == MoEActivation.SWIGLUOAI_UNINTERLEAVE:
  110. if self.swiglu_limit is None:
  111. raise ValueError(
  112. "swigluoai_uninterleave requires swiglu_limit "
  113. "(gemm1_clamp_limit), but none was provided."
  114. )
  115. elif self.swiglu_limit is not None:
  116. raise ValueError(
  117. "FlashInferB12xExperts only applies swiglu_limit with the "
  118. f"swigluoai_uninterleave activation, got {activation}."
  119. )
  120. """
  121. text = replace_once(
  122. text,
  123. " self._activation_str = self._ACTIVATION_MAP[activation]\n",
  124. " self._activation_str = self._ACTIVATION_MAP[activation]\n"
  125. + config_block,
  126. "B12x activation configuration",
  127. )
  128. support_check = " and has_flashinfer_b12x_moe_activation()\n"
  129. if support_check not in text:
  130. legacy_support = (
  131. " def _supports_activation(activation: MoEActivation) -> bool:\n"
  132. " return activation in ("
  133. "MoEActivation.SILU, MoEActivation.RELU2_NO_MUL)\n"
  134. )
  135. patched_legacy_support = (
  136. " def _supports_activation(activation: MoEActivation) -> bool:\n"
  137. " if activation in ("
  138. "MoEActivation.SILU, MoEActivation.RELU2_NO_MUL):\n"
  139. " return True\n"
  140. " return (\n"
  141. " activation == MoEActivation.SWIGLUOAI_UNINTERLEAVE\n"
  142. + support_check
  143. + " )\n"
  144. )
  145. gelu_support = (
  146. " def _supports_activation(activation: MoEActivation) -> bool:\n"
  147. " return activation in (\n"
  148. " MoEActivation.SILU,\n"
  149. " MoEActivation.GELU_TANH,\n"
  150. " MoEActivation.RELU2_NO_MUL,\n"
  151. " )\n"
  152. )
  153. patched_gelu_support = (
  154. " def _supports_activation(activation: MoEActivation) -> bool:\n"
  155. " if activation in (\n"
  156. " MoEActivation.SILU,\n"
  157. " MoEActivation.GELU_TANH,\n"
  158. " MoEActivation.RELU2_NO_MUL,\n"
  159. " ):\n"
  160. " return True\n"
  161. " return (\n"
  162. " activation == MoEActivation.SWIGLUOAI_UNINTERLEAVE\n"
  163. + support_check
  164. + " )\n"
  165. )
  166. known_support_shapes = (
  167. (legacy_support, patched_legacy_support),
  168. (gelu_support, patched_gelu_support),
  169. )
  170. matches = [(old, new) for old, new in known_support_shapes if old in text]
  171. match_count = sum(text.count(old) for old, _ in known_support_shapes)
  172. if match_count != 1:
  173. raise PatchError(
  174. "expected one B12x activation support predicate source anchor, "
  175. f"found {match_count}; the vLLM source shape has changed"
  176. )
  177. old_support, new_support = matches[0]
  178. text = text.replace(old_support, new_support, 1)
  179. if " swiglu_kwargs: dict[str, float] = {}\n" not in text:
  180. wrapper_kwargs = """ swiglu_kwargs: dict[str, float] = {}
  181. if self.swiglu_limit is not None:
  182. # vLLM defaults unset alpha/beta to 1.0/0.0, while FlashInfer's
  183. # wrapper defaults are 1.702/1.0, so pass the resolved values.
  184. swiglu_kwargs = {
  185. "swiglu_limit": self.swiglu_limit,
  186. "swiglu_alpha": (
  187. 1.0 if self.swiglu_alpha is None else self.swiglu_alpha
  188. ),
  189. "swiglu_beta": (
  190. 0.0 if self.swiglu_beta is None else self.swiglu_beta
  191. ),
  192. }
  193. """
  194. text = replace_once(
  195. text,
  196. " self._wrapper = B12xMoEWrapper(\n",
  197. wrapper_kwargs + " self._wrapper = B12xMoEWrapper(\n",
  198. "B12x wrapper construction",
  199. )
  200. if " **swiglu_kwargs,\n" not in text:
  201. text = replace_once(
  202. text,
  203. " activation=self._activation_str,\n",
  204. " activation=self._activation_str,\n"
  205. " **swiglu_kwargs,\n",
  206. "B12x wrapper SwiGLU keyword forwarding",
  207. )
  208. required = (
  209. helper_import,
  210. activation_entry,
  211. " self.swiglu_alpha = _resolve(",
  212. support_check,
  213. " **swiglu_kwargs,\n",
  214. )
  215. missing = [marker.strip() for marker in required if marker not in text]
  216. if missing:
  217. raise PatchError(f"FlashInferB12xExperts patch is incomplete: {missing}")
  218. return text
  219. def patch_oracle(text: str) -> str:
  220. activation_import = (
  221. "from vllm.model_executor.layers.fused_moe.activation import "
  222. "MoEActivation\n"
  223. )
  224. if activation_import not in text:
  225. text = replace_once(
  226. text,
  227. "from vllm.logger import init_logger\n",
  228. "from vllm.logger import init_logger\n" + activation_import,
  229. "MoEActivation import",
  230. )
  231. helper_import = (
  232. "from vllm.utils.flashinfer import "
  233. "has_flashinfer_b12x_moe_activation\n"
  234. )
  235. if helper_import not in text:
  236. text = replace_once(
  237. text,
  238. "\nlogger = init_logger(__name__)\n",
  239. helper_import + "\nlogger = init_logger(__name__)\n",
  240. "B12x activation helper import",
  241. )
  242. set_marker = " NVFP4_BACKENDS_WITH_CLAMP = {\n"
  243. set_start = text.find(set_marker)
  244. if set_start == -1:
  245. raise PatchError("NVFP4_BACKENDS_WITH_CLAMP is absent from the oracle")
  246. set_end = text.find("\n }", set_start + len(set_marker))
  247. if set_end == -1:
  248. raise PatchError("NVFP4_BACKENDS_WITH_CLAMP has an unknown source shape")
  249. set_end += len("\n }")
  250. clamp_set = text[set_start:set_end]
  251. add_marker = (
  252. "NVFP4_BACKENDS_WITH_CLAMP.add("
  253. "NvFp4MoeBackend.FLASHINFER_B12X)"
  254. )
  255. if (
  256. "NvFp4MoeBackend.FLASHINFER_B12X" not in clamp_set
  257. and add_marker not in text
  258. ):
  259. condition = """
  260. # B12x applies the clamp only for SwiGLU-OAI and only when the installed
  261. # FlashInfer wrapper exposes the corresponding activation parameters.
  262. if (
  263. config.activation == MoEActivation.SWIGLUOAI_UNINTERLEAVE
  264. and has_flashinfer_b12x_moe_activation()
  265. ):
  266. NVFP4_BACKENDS_WITH_CLAMP.add(NvFp4MoeBackend.FLASHINFER_B12X)"""
  267. text = text[:set_end] + condition + text[set_end:]
  268. set_start = text.find(set_marker)
  269. set_end = text.find("\n }", set_start + len(set_marker)) + len("\n }")
  270. clamp_set = text[set_start:set_end]
  271. if (
  272. "NvFp4MoeBackend.FLASHINFER_B12X" not in clamp_set
  273. and add_marker not in text
  274. ):
  275. raise PatchError("NVFP4 oracle patch is incomplete")
  276. return text
  277. def main() -> int:
  278. source_root = Path(sys.argv[1]) if len(sys.argv) > 1 else Path.cwd()
  279. expert_path = source_root / EXPERT_REL
  280. if not expert_path.exists():
  281. print(f"{EXPERT_REL} is absent; targeted PR #47392 patch is not applicable")
  282. return 0
  283. paths_and_patchers = (
  284. (source_root / FLASHINFER_UTIL_REL, patch_flashinfer_util),
  285. (expert_path, patch_expert),
  286. (source_root / ORACLE_REL, patch_oracle),
  287. )
  288. updates: list[tuple[Path, str, str]] = []
  289. for path, patcher in paths_and_patchers:
  290. if not path.exists():
  291. raise PatchError(f"{path} is missing while {EXPERT_REL} exists")
  292. original = path.read_text()
  293. updated = patcher(original)
  294. compile(updated, str(path), "exec")
  295. updates.append((path, original, updated))
  296. changed = []
  297. for path, original, updated in updates:
  298. if updated != original:
  299. path.write_text(updated)
  300. changed.append(str(path.relative_to(source_root)))
  301. if changed:
  302. print("Applied targeted vLLM PR #47392 runtime patch: " + ", ".join(changed))
  303. else:
  304. print("Targeted vLLM PR #47392 runtime behavior is already present")
  305. return 0
  306. if __name__ == "__main__":
  307. try:
  308. raise SystemExit(main())
  309. except PatchError as exc:
  310. raise SystemExit(f"targeted vLLM PR #47392 patch failed: {exc}") from exc