patch_model_loader.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. #!/usr/bin/env python3
  2. """Add an opt-in hybrid loader policy for speculative draft models."""
  3. from __future__ import annotations
  4. import argparse
  5. import ast
  6. import sys
  7. from pathlib import Path
  8. MARKER = "# spark-vllm mod: instanttensor-hybrid-draft-loader v1"
  9. GET_MODEL_ANCHOR = """
  10. def get_model(
  11. *,
  12. vllm_config: VllmConfig,
  13. model_config: ModelConfig | None = None,
  14. prefix: str = "",
  15. load_config: LoadConfig | None = None,
  16. ) -> nn.Module:
  17. loader = get_model_loader(load_config or vllm_config.load_config)
  18. if model_config is None:
  19. model_config = vllm_config.model_config
  20. return loader.load_model(
  21. vllm_config=vllm_config, model_config=model_config, prefix=prefix
  22. )
  23. """
  24. GET_MODEL_REPLACEMENT = f"""
  25. {MARKER}
  26. def _instanttensor_draft_load_config(
  27. vllm_config: VllmConfig,
  28. model_config: ModelConfig,
  29. load_config: LoadConfig | None,
  30. ) -> LoadConfig:
  31. \"\"\"Resolve the loader for one model without changing the target loader.\"\"\"
  32. import os
  33. from vllm.config import replace
  34. mode = os.environ.get("INSTANTTENSOR_DRAFT_LOADER", "auto").strip().lower()
  35. allowed_modes = ("auto", "safetensors", "instanttensor")
  36. if mode not in allowed_modes:
  37. raise ValueError(
  38. "INSTANTTENSOR_DRAFT_LOADER must be one of "
  39. f"{{', '.join(allowed_modes)}}; got {{mode!r}}"
  40. )
  41. effective = load_config or vllm_config.load_config
  42. load_format = getattr(effective.load_format, "value", effective.load_format)
  43. if mode == "instanttensor" or str(load_format).lower() != "instanttensor":
  44. return effective
  45. speculative_config = getattr(vllm_config, "speculative_config", None)
  46. draft_model_config = getattr(speculative_config, "draft_model_config", None)
  47. if draft_model_config is None or model_config is not draft_model_config:
  48. return effective
  49. if mode == "auto":
  50. target_model_config = (
  51. getattr(speculative_config, "target_model_config", None)
  52. or vllm_config.model_config
  53. )
  54. draft_source = (
  55. getattr(draft_model_config, "model", None),
  56. getattr(draft_model_config, "revision", None),
  57. )
  58. target_source = (
  59. getattr(target_model_config, "model", None),
  60. getattr(target_model_config, "revision", None),
  61. )
  62. if draft_source != target_source:
  63. return effective
  64. logger.info_once(
  65. "Hybrid draft loading: using lazy safetensors for speculative draft "
  66. "weights while preserving InstantTensor for the target model "
  67. "(INSTANTTENSOR_DRAFT_LOADER=%s).",
  68. mode,
  69. )
  70. return replace(
  71. effective,
  72. load_format="safetensors",
  73. safetensors_load_strategy="lazy",
  74. )
  75. def get_model(
  76. *,
  77. vllm_config: VllmConfig,
  78. model_config: ModelConfig | None = None,
  79. prefix: str = "",
  80. load_config: LoadConfig | None = None,
  81. ) -> nn.Module:
  82. if model_config is None:
  83. model_config = vllm_config.model_config
  84. resolved_load_config = _instanttensor_draft_load_config(
  85. vllm_config, model_config, load_config
  86. )
  87. loader = get_model_loader(resolved_load_config)
  88. return loader.load_model(
  89. vllm_config=vllm_config, model_config=model_config, prefix=prefix
  90. )
  91. """
  92. def validate_shape(text: str, *, patched: bool) -> None:
  93. tree = ast.parse(text)
  94. functions = {
  95. node.name
  96. for node in tree.body
  97. if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
  98. }
  99. required = {"get_model", "get_model_loader"}
  100. if patched:
  101. required.add("_instanttensor_draft_load_config")
  102. missing = sorted(required - functions)
  103. if missing:
  104. raise ValueError(
  105. "unexpected model-loader module; missing " + ", ".join(missing)
  106. )
  107. def patched_text(text: str) -> str:
  108. validate_shape(text, patched=MARKER in text)
  109. if MARKER in text:
  110. if text.count(MARKER) != 1:
  111. raise ValueError("hybrid-loader marker occurs more than once")
  112. if "resolved_load_config = _instanttensor_draft_load_config(" not in text:
  113. raise ValueError("mod marker exists but get_model does not use the helper")
  114. compile(text, "<patched model_loader/__init__.py>", "exec")
  115. return text
  116. count = text.count(GET_MODEL_ANCHOR)
  117. if count != 1:
  118. raise ValueError(
  119. f"expected exactly one supported get_model function; found {count}"
  120. )
  121. patched = text.replace(GET_MODEL_ANCHOR, GET_MODEL_REPLACEMENT, 1)
  122. validate_shape(patched, patched=True)
  123. compile(patched, "<patched model_loader/__init__.py>", "exec")
  124. return patched
  125. def main() -> int:
  126. parser = argparse.ArgumentParser()
  127. parser.add_argument("target", type=Path)
  128. parser.add_argument(
  129. "--check", action="store_true", help="validate compatibility without writing"
  130. )
  131. args = parser.parse_args()
  132. if not args.target.is_file():
  133. print(
  134. "[instanttensor-hybrid-draft-loader ERROR] "
  135. f"target not found: {args.target}",
  136. file=sys.stderr,
  137. )
  138. return 1
  139. original = args.target.read_text()
  140. try:
  141. patched = patched_text(original)
  142. except (SyntaxError, ValueError) as exc:
  143. print(
  144. "[instanttensor-hybrid-draft-loader ERROR] "
  145. f"refusing to patch {args.target}: {exc}",
  146. file=sys.stderr,
  147. )
  148. return 1
  149. if args.check:
  150. state = "already patched" if patched == original else "compatible"
  151. print(f"[instanttensor-hybrid-draft-loader] {args.target} is {state}.")
  152. return 0
  153. if patched == original:
  154. print(
  155. "[instanttensor-hybrid-draft-loader] "
  156. "Model loader is already patched; skipping."
  157. )
  158. return 0
  159. temporary = args.target.with_suffix(
  160. args.target.suffix + ".instanttensor-draft-mod.tmp"
  161. )
  162. temporary.write_text(patched)
  163. temporary.replace(args.target)
  164. print(f"[instanttensor-hybrid-draft-loader] Patched {args.target}.")
  165. return 0
  166. if __name__ == "__main__":
  167. raise SystemExit(main())