Prechádzať zdrojové kódy

feat(deepseek-v4-eugr): optional reasoning_content key patch (default-off)

Adds a separate, independently-toggleable patch so eugr's vendored
deepseek_v4_encoding.py reads the newer "reasoning_content" message key
instead of the deprecated "reasoning" key.

- DEFAULT-OFF: gated by VLLM_DSV4_REASONING_CONTENT env (1/true/yes/on),
  so the first-test variant remains the pure minimal reasoning_effort change
  (zero behavior change until opted in). Backward compatible: uses
  reasoning_content only when the message actually carries that key, else
  falls back to reasoning.
- Three tiny edits: module-level env flag + import os; render_message read;
  drop_thinking also pops reasoning_content (no-op when unused).
- Idempotent --check (pristine or already-patched) + apply + post-check,
  wired into its own commented RUN block in the Dockerfile (comment it out to
  remove entirely, or set the env var at runtime to enable).

Also hardens _consistent() in both patchers to handle edits where NEW embeds
OLD (e.g. an inserted import block starting with the original lines), and
fixes the rc patcher main() to actually read the file (was unpacking the
edits list into src).
Alexander Korolev 2 mesiacov pred
rodič
commit
070bf6666a

+ 13 - 0
docker-images/deepseek-v4-flash-eugr/Dockerfile

@@ -53,3 +53,16 @@ RUN set -eux; \
     python3 /tmp/patch_deepseek_v4_effort.py --check; \
     find "$(python3 -c 'import vllm, os; print(os.path.join(vllm.__path__[0], "tokenizers"))')" -name '__pycache__' -type d -prune -exec rm -rf {} + 2>/dev/null || true; \
     rm -f /tmp/patch_deepseek_v4_effort.py
+
+# Optional: read the newer `reasoning_content` message key instead of the
+# deprecated `reasoning` key. DEFAULT-OFF — the first-test variant stays the
+# pure reasoning_effort change above. Turn on at runtime with
+# VLLM_DSV4_REASONING_CONTENT=1 (recipe [env] / -e); or comment this block to
+# remove entirely. See patches/patch_deepseek_v4_reasoning_content.py.
+COPY patches/patch_deepseek_v4_reasoning_content.py /tmp/patch_dsv4_rc.py
+RUN set -eux; \
+    python3 /tmp/patch_dsv4_rc.py --check; \
+    python3 /tmp/patch_dsv4_rc.py; \
+    python3 /tmp/patch_dsv4_rc.py --check; \
+    find "$(python3 -c 'import vllm, os; print(os.path.join(vllm.__path__[0], "tokenizers"))')" -name '__pycache__' -type d -prune -exec rm -rf {} + 2>/dev/null || true; \
+    rm -f /tmp/patch_dsv4_rc.py

+ 8 - 2
docker-images/deepseek-v4-flash-eugr/patches/patch_deepseek_v4_effort.py

@@ -97,9 +97,15 @@ TARGETS = [
 
 
 def _consistent(src, old, new):
-    """True if the file holds exactly one of old|new (pristine or patched)."""
+    """True if the file holds exactly one of old|new (pristine or patched).
+    Handles overlapping edits where NEW embeds OLD (e.g. an inserted import
+    block that starts with the original lines)."""
     co, cn = src.count(old), src.count(new)
-    return (co == 1 and cn == 0) or (co == 0 and cn == 1)
+    if cn >= 1:
+        # patched: new present once, and old does not appear beyond the copy
+        # embedded inside new itself.
+        return cn == 1 and co <= new.count(old)
+    return co == 1
 
 
 def main() -> None:

+ 120 - 0
docker-images/deepseek-v4-flash-eugr/patches/patch_deepseek_v4_reasoning_content.py

@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+"""Optional patch to the eugr image's vendored deepseek_v4_encoding.py to read
+the newer `reasoning_content` message key instead of the deprecated `reasoning`
+key.
+
+This is INDEPENDENT of the reasoning_effort patch and is DEFAULT-OFF so the
+pure reasoning_effort (minimal) variant is the first-test behavior. Enable at
+runtime with VLLM_DSV4_REASONING_CONTENT=1 (recipe [env] / -e). It stays
+backward compatible: when enabled it uses `reasoning_content` only when the
+message actually has that key, otherwise falls back to `reasoning`.
+
+Three small edits on top of whatever eugr ships:
+  1. add a module-level env-gated flag (_USE_REASONING_CONTENT_KEY) + import os
+  2. render_message: use reasoning_content (fallback reasoning) when enabled
+  3. drop_thinking: also pop reasoning_content (no-op when key unused)
+
+Everything else in eugr's file is left intact, so no independent updates are
+lost. Idempotent --check (pristine or already-patched) + apply + post-check,
+mirroring patch_deepseek_v4_effort.py.
+
+Usage:  python3 patch_deepseek_v4_reasoning_content.py [--check]
+  --check : verify the file is in a consistent (pristine or patched) state.
+  (apply) : replace old anchors with new (requires exact old-anchor match).
+"""
+import os
+import sys
+
+import vllm
+
+BASE = os.path.join(vllm.__path__[0], "tokenizers")
+F_ENC = os.path.join(BASE, "deepseek_v4_encoding.py")
+
+# 1) import os + env-gated module flag (inserted after the base imports)
+OLD_IMPORTS = (
+    "from typing import Any, Dict, List, Union, Optional\n"
+    "import copy\n"
+    "import json"
+)
+NEW_IMPORTS = (
+    "from typing import Any, Dict, List, Union, Optional\n"
+    "import copy\n"
+    "import json\n"
+    "import os\n"
+    "\n"
+    "# Optional: read the newer `reasoning_content` message key instead of the\n"
+    "# deprecated `reasoning` key. Disabled by default (preserves eugr\n"
+    "# behavior); enable with VLLM_DSV4_REASONING_CONTENT=1.\n"
+    "_USE_REASONING_CONTENT_KEY = bool(\n"
+    "    os.environ.get(\"VLLM_DSV4_REASONING_CONTENT\", \"\").strip().lower()\n"
+    "    in (\"1\", \"true\", \"yes\", \"on\")\n"
+    ")"
+)
+
+# 2) render_message: use reasoning_content when enabled (fallback to reasoning)
+OLD_READ = "    reasoning = msg.get(\"reasoning\")"
+NEW_READ = (
+    "    reasoning = msg.get(\"reasoning\")\n"
+    "    if _USE_REASONING_CONTENT_KEY and \"reasoning_content\" in msg:\n"
+    "        reasoning = msg.get(\"reasoning_content\")"
+)
+
+# 3) drop_thinking: also pop reasoning_content (no-op when key unused)
+OLD_POP = "            msg.pop(\"reasoning\", None)"
+NEW_POP = (
+    "            msg.pop(\"reasoning_content\", None)\n"
+    "            msg.pop(\"reasoning\", None)"
+)
+
+TARGETS = [(F_ENC, [(OLD_IMPORTS, NEW_IMPORTS), (OLD_READ, NEW_READ),
+                    (OLD_POP, NEW_POP)])]
+
+
+def _consistent(src, old, new):
+    """True if the file holds exactly one of old|new (pristine or patched).
+    Handles overlapping edits where NEW embeds OLD (e.g. an inserted import
+    block that starts with the original lines)."""
+    co, cn = src.count(old), src.count(new)
+    if cn >= 1:
+        # patched: new present once, and old does not appear beyond the copy
+        # embedded inside new itself.
+        return cn == 1 and co <= new.count(old)
+    return co == 1
+
+
+def main() -> None:
+    check = "--check" in sys.argv
+    path = TARGETS[0][0]
+    src = open(path).read()
+
+    if check:
+        for old, new in TARGETS[0][1]:
+            if not _consistent(src, old, new):
+                raise SystemExit(
+                    f"CHECK FAIL: {path} — neither pristine-old nor "
+                    "already-patched for an anchor"
+                )
+        print("check OK: deepseek_v4 reasoning_content state is consistent")
+        return
+
+    for old, new in TARGETS[0][1]:
+        if src.count(old) != 1:
+            raise SystemExit(
+                f"refusing: {path} old anchor matched {src.count(old)}x "
+                "(expected 1); source changed, re-sync patch"
+            )
+    for old, new in TARGETS[0][1]:
+        src = src.replace(old, new)
+    for old, new in TARGETS[0][1]:
+        if src.count(new) != 1:
+            raise SystemExit(
+                f"refusing: {path} new anchor present {src.count(new)}x "
+                "after apply (internal error)"
+            )
+    with open(path, "w") as f:
+        f.write(src)
+    print(f"patched {path}")
+
+
+if __name__ == "__main__":
+    main()