verify-base.py 4.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. #!/usr/bin/env python3
  2. """Assert the image carries everything glm53 depends on, AFTER the patches ran.
  3. Each item here has failed silently on this model before -- the image booted,
  4. loaded ~90 GiB per node over ten minutes, and then died or served nonsense.
  5. Failing the BUILD is far cheaper. Checks both what the nightly must provide
  6. and that every patch actually landed.
  7. """
  8. import importlib
  9. import subprocess
  10. import sys
  11. from pathlib import Path
  12. import torch
  13. from vllm.model_executor.models.registry import ModelRegistry
  14. from vllm.reasoning import ReasoningParserManager
  15. from vllm.tool_parsers import ToolParserManager
  16. V = Path("/usr/local/lib/python3.12/dist-packages/vllm")
  17. problems = []
  18. archs = set(ModelRegistry.get_supported_archs())
  19. if "Glm5NextForConditionalGeneration" not in archs:
  20. problems.append("Glm5NextForConditionalGeneration missing from the model registry")
  21. try:
  22. ReasoningParserManager.get_reasoning_parser("glm45")
  23. except Exception as e:
  24. problems.append(f"reasoning parser glm45 unavailable: {e!r}")
  25. # The fail-closed tool parser is a plugin file, so load it the way
  26. # --tool-parser-plugin will, then ask for it by name.
  27. try:
  28. ToolParserManager.import_tool_parser("/usr/local/share/glm47_failclosed.py")
  29. ToolParserManager.get_tool_parser("glm47_failclosed")
  30. except Exception as e:
  31. problems.append(f"glm47_failclosed plugin does not register: {e!r}")
  32. if "sm_120" not in torch.cuda.get_arch_list():
  33. problems.append(f"no sm_120 cubins in torch {torch.__version__}")
  34. # Patches landed. MiaAI's and the spin-wait patcher assert their own anchors;
  35. # these re-read the result so a patcher that silently no-ops is caught too.
  36. cuda_py = (V / "platforms/cuda.py").read_text()
  37. if "FLASHINFER_MLA_SPARSE_SM90" not in cuda_py.split("device_capability.major == 12")[1][:600]:
  38. problems.append("capability 12 does not list FLASHINFER_MLA_SPARSE_SM90 (MiaAI patch)")
  39. sm90 = (V / "v1/attention/backends/mla/flashinfer_mla_sparse_sm90.py").read_text()
  40. if "capability.major == 9\n" in sm90:
  41. problems.append("SM90 sparse backend still Hopper-only (MiaAI patch)")
  42. idx = (V / "models/glm5next/nvidia/sparse_indexer.py").read_text()
  43. if "pool_topk = torch.empty(" in idx:
  44. problems.append("indexer pool_topk still torch.empty (MiaAI indexer -1, retargeted)")
  45. if "SM90-FP8-KV-DTYPE" not in sm90:
  46. problems.append("SM90 builder still plans fp8 KV as uint8 (sm90_fp8_kv_dtype.py)")
  47. if "GLM53-DFLASH2-KV" not in (V / "v1/core/kv_cache_utils.py").read_text():
  48. problems.append("GLM-5-Next KV grouper cannot carry drafter layers (glm53_dflash2_kv_groups.py)")
  49. if "GLM53-MTP-BF16" not in (V / "models/glm5next/common/mtp.py").read_text():
  50. problems.append("MTP bf16 exclusion missing (glm53_mtp_bf16.py)")
  51. if "SupportsEagle3" not in (V / "models/glm5next/common/model.py").read_text():
  52. problems.append("GLM5next has no aux hidden states (glm53_eagle3_aux.py)")
  53. if "vllm_config.num_speculative_tokens" not in (V / "models/glm5next/common/attention.py").read_text():
  54. problems.append("kpool tail ring not sized for spec decode (vllm#58454 missing from the base)")
  55. if "_index_expert_mapping" not in (V / "model_executor/layers/fused_moe/routed_experts.py").read_text():
  56. problems.append("RoutedExperts scans the expert mapping per tensor (vllm#58720, expected in the base)")
  57. if not (V / "FLASHKDA_REF").read_text().startswith("17a037d"):
  58. problems.append("_flashkda_C is not FlashKDA 17a037d (vllm#58846 builder stage)")
  59. if "GLM53-REASONING-ALWAYS" not in (V / "parser/glm47_moe.py").read_text():
  60. problems.append("reasoning parser drops <think> when thinking is off (glm53_reasoning_always_parsed.py)")
  61. if "busy_loop_s: float = 0.002," not in (V / "distributed/device_communicators/shm_broadcast.py").read_text():
  62. problems.append("shm_broadcast busy_loop_s not 0.002 (spin_wait.py)")
  63. # Multi-node TP needs the real mentat daemon behind the `ray` executable, not
  64. # just the shim package: ranks are placed through it.
  65. try:
  66. out = subprocess.run(["ray", "--version"], capture_output=True, text=True, timeout=30).stdout
  67. if "mentat" not in out.lower():
  68. problems.append(f"`ray` is not mentat: {out.strip()[:120]}")
  69. except Exception as e:
  70. problems.append(f"`ray` executable missing: {e!r}")
  71. # The shim package, whose ray.register module is how a TP=1 stack joins
  72. # without the daemon binary. Missing, a container starts and serves and never
  73. # appears in the router.
  74. try:
  75. importlib.import_module("ray.register")
  76. except Exception as e:
  77. problems.append(f"mentat ray.register not importable: {e!r}")
  78. import shutil
  79. if not shutil.which("mentatd-probe-machine"):
  80. problems.append("mentatd-probe-machine missing: `ray start` would register zero GPUs")
  81. if problems:
  82. for p in problems:
  83. print(f"IMAGE CHECK FAILED: {p}", file=sys.stderr)
  84. sys.exit(1)
  85. print("image ok: Glm5Next, glm45, glm47_failclosed, sm_120, MiaAI SM90 + indexer, spin-wait, FlashKDA 17a037d, mentat ray + ray.register")