patch_deepseek_v4_reasoning_content.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. #!/usr/bin/env python3
  2. """Optional patch to the eugr image's vendored deepseek_v4_encoding.py to read
  3. the newer `reasoning_content` message key instead of the deprecated `reasoning`
  4. key.
  5. This is INDEPENDENT of the reasoning_effort patch and is DEFAULT-OFF so the
  6. pure reasoning_effort (minimal) variant is the first-test behavior. Enable at
  7. runtime with VLLM_DSV4_REASONING_CONTENT=1 (recipe [env] / -e). It stays
  8. backward compatible: when enabled it uses `reasoning_content` only when the
  9. message actually has that key, otherwise falls back to `reasoning`.
  10. Three small edits on top of whatever eugr ships:
  11. 1. add a module-level env-gated flag (_USE_REASONING_CONTENT_KEY) + import os
  12. 2. render_message: use reasoning_content (fallback reasoning) when enabled
  13. 3. drop_thinking: also pop reasoning_content (no-op when key unused)
  14. Everything else in eugr's file is left intact, so no independent updates are
  15. lost. Idempotent --check (pristine or already-patched) + apply + post-check,
  16. mirroring patch_deepseek_v4_effort.py.
  17. Usage: python3 patch_deepseek_v4_reasoning_content.py [--check]
  18. --check : verify the file is in a consistent (pristine or patched) state.
  19. (apply) : replace old anchors with new (requires exact old-anchor match).
  20. """
  21. import os
  22. import sys
  23. import vllm
  24. BASE = os.path.join(vllm.__path__[0], "tokenizers")
  25. F_ENC = os.path.join(BASE, "deepseek_v4_encoding.py")
  26. # 1) import os + env-gated module flag (inserted after the base imports)
  27. OLD_IMPORTS = (
  28. "from typing import Any, Dict, List, Union, Optional\n"
  29. "import copy\n"
  30. "import json"
  31. )
  32. NEW_IMPORTS = (
  33. "from typing import Any, Dict, List, Union, Optional\n"
  34. "import copy\n"
  35. "import json\n"
  36. "import os\n"
  37. "\n"
  38. "# Optional: read the newer `reasoning_content` message key instead of the\n"
  39. "# deprecated `reasoning` key. Disabled by default (preserves eugr\n"
  40. "# behavior); enable with VLLM_DSV4_REASONING_CONTENT=1.\n"
  41. "_USE_REASONING_CONTENT_KEY = bool(\n"
  42. " os.environ.get(\"VLLM_DSV4_REASONING_CONTENT\", \"\").strip().lower()\n"
  43. " in (\"1\", \"true\", \"yes\", \"on\")\n"
  44. ")"
  45. )
  46. # 2) render_message: use reasoning_content when enabled (fallback to reasoning)
  47. OLD_READ = " reasoning = msg.get(\"reasoning\")"
  48. NEW_READ = (
  49. " reasoning = msg.get(\"reasoning\")\n"
  50. " if _USE_REASONING_CONTENT_KEY and \"reasoning_content\" in msg:\n"
  51. " reasoning = msg.get(\"reasoning_content\")"
  52. )
  53. # 3) drop_thinking: also pop reasoning_content (no-op when key unused)
  54. OLD_POP = " msg.pop(\"reasoning\", None)"
  55. NEW_POP = (
  56. " msg.pop(\"reasoning_content\", None)\n"
  57. " msg.pop(\"reasoning\", None)"
  58. )
  59. TARGETS = [(F_ENC, [(OLD_IMPORTS, NEW_IMPORTS), (OLD_READ, NEW_READ),
  60. (OLD_POP, NEW_POP)])]
  61. def _consistent(src, old, new):
  62. """True if the file holds exactly one of old|new (pristine or patched).
  63. Handles overlapping edits where NEW embeds OLD (e.g. an inserted import
  64. block that starts with the original lines)."""
  65. co, cn = src.count(old), src.count(new)
  66. if cn >= 1:
  67. # patched: new present once, and old does not appear beyond the copy
  68. # embedded inside new itself.
  69. return cn == 1 and co <= new.count(old)
  70. return co == 1
  71. def main() -> None:
  72. check = "--check" in sys.argv
  73. path = TARGETS[0][0]
  74. src = open(path).read()
  75. if check:
  76. for old, new in TARGETS[0][1]:
  77. if not _consistent(src, old, new):
  78. raise SystemExit(
  79. f"CHECK FAIL: {path} — neither pristine-old nor "
  80. "already-patched for an anchor"
  81. )
  82. print("check OK: deepseek_v4 reasoning_content state is consistent")
  83. return
  84. for old, new in TARGETS[0][1]:
  85. if src.count(old) != 1:
  86. raise SystemExit(
  87. f"refusing: {path} old anchor matched {src.count(old)}x "
  88. "(expected 1); source changed, re-sync patch"
  89. )
  90. for old, new in TARGETS[0][1]:
  91. src = src.replace(old, new)
  92. for old, new in TARGETS[0][1]:
  93. if src.count(new) != 1:
  94. raise SystemExit(
  95. f"refusing: {path} new anchor present {src.count(new)}x "
  96. "after apply (internal error)"
  97. )
  98. with open(path, "w") as f:
  99. f.write(src)
  100. print(f"patched {path}")
  101. if __name__ == "__main__":
  102. main()