docs: upstream auto-disable for CausalLM docstring patch
This commit is contained in:
@@ -148,359 +148,32 @@ Unchanged for this fix: `patches/nunchaku_patch.py` (separate v2.4.6 `apply_rota
|
||||
|
||||
---
|
||||
|
||||
## 4. Full added / modified source code
|
||||
## 4. Source files (canonical)
|
||||
|
||||
### 4.1 `patches/transformers_qwen_vl_docstring_patch.py` (complete)
|
||||
Full implementation lives in the repository (not duplicated here, to avoid stale copies).
|
||||
|
||||
```python
|
||||
"""
|
||||
Runtime patch for transformers Qwen VL CausalLM ModelOutput @auto_docstring errors.
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `patches/transformers_qwen_vl_docstring_patch.py` | Upstream probes; optional wrap of `get_args_doc_from_source` |
|
||||
| `prestartup_script.py` | Loads docstring patch via `importlib` **before** rotary compat |
|
||||
|
||||
At import time, @auto_docstring on Qwen3VLCausalLMOutputWithPast and
|
||||
Qwen2_5_VLCausalLMOutputWithPast validates __init__ parameters against
|
||||
documentation built from ModelOutputArgs. That template omits loss/logits
|
||||
even though subclasses inherit those fields from CausalLMOutputWithPast.
|
||||
Public API:
|
||||
|
||||
Fix: wrap get_args_doc_from_source so ModelOutputArgs merges loss/logits
|
||||
from CausalLMOutputWithPast.__doc__ (parse_docstring max_indent_level=4).
|
||||
| Function | Returns |
|
||||
|----------|---------|
|
||||
| `apply_transformers_causal_lm_docstring_patch()` | `True` if wrapper active; `False` if skipped (upstream fixed or late import) |
|
||||
| `is_patch_applied()` | Whether this process installed the wrapper |
|
||||
| `is_patch_wrapped()` | Whether `get_args_doc_from_source` carries the patch tag |
|
||||
|
||||
Self-disables when upstream ModelOutputArgs already documents loss+logits,
|
||||
when env TRANSFORMERS_CAUSAL_LM_DOCSTRING_PATCH disables the patch, or when
|
||||
a subprocess import probe shows no [ERROR] loss/logits without the patch.
|
||||
`apply_transformers_causal_lm_docstring_patch()` decision order:
|
||||
|
||||
No site-packages edits. Idempotent via _qwen_lora_loader_causal_lm_docstring_patch.
|
||||
"""
|
||||
1. Already applied → return `True`
|
||||
2. Qwen VL modeling already in `sys.modules` → warn, return `False` (restart ComfyUI)
|
||||
3. `ModelOutputArgs` already documents `loss` and `logits` → skip (upstream fixed)
|
||||
4. Subprocess import probe prints `CLEAN` → skip (no docstring errors without patch)
|
||||
5. Else install wrapper around `get_args_doc_from_source`
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import io
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from contextlib import redirect_stdout
|
||||
from typing import Any, Callable
|
||||
|
||||
_PATCH_TAG = "_qwen_lora_loader_causal_lm_docstring_patch"
|
||||
_ENV_DISABLE = "TRANSFORMERS_CAUSAL_LM_DOCSTRING_PATCH"
|
||||
_DISABLE_VALUES = frozenset({"0", "false", "off", "no", "disable", "disabled"})
|
||||
|
||||
_QWEN3_MODULE = "transformers.models.qwen3_vl.modeling_qwen3_vl"
|
||||
_QWEN25_MODULE = "transformers.models.qwen2_5_vl.modeling_qwen2_5_vl"
|
||||
_QWEN3_CLASS = "Qwen3VLCausalLMOutputWithPast"
|
||||
_QWEN25_CLASS = "Qwen2_5_VLCausalLMOutputWithPast"
|
||||
|
||||
_ERROR_MARKERS = (
|
||||
"[ERROR] `loss`",
|
||||
"[ERROR] `logits`",
|
||||
)
|
||||
|
||||
|
||||
def _env_disabled() -> bool:
|
||||
return os.environ.get(_ENV_DISABLE, "").strip().lower() in _DISABLE_VALUES
|
||||
|
||||
|
||||
def _get_auto_docstring_module():
|
||||
return importlib.import_module("transformers.utils.auto_docstring")
|
||||
|
||||
|
||||
def _get_model_output_args_class(auto_mod):
|
||||
return getattr(auto_mod, "ModelOutputArgs", None)
|
||||
|
||||
|
||||
def _get_causal_lm_output_with_past(auto_mod):
|
||||
try:
|
||||
from transformers.modeling_outputs import CausalLMOutputWithPast
|
||||
return CausalLMOutputWithPast
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
|
||||
def _parse_docstring(auto_mod, docstring: str, max_indent_level: int = 4) -> dict:
|
||||
parse_fn = getattr(auto_mod, "parse_docstring", None)
|
||||
if parse_fn is None:
|
||||
return {}
|
||||
result = parse_fn(docstring, max_indent_level=max_indent_level)
|
||||
if isinstance(result, tuple):
|
||||
params = result[0]
|
||||
return params if isinstance(params, dict) else {}
|
||||
return result if isinstance(result, dict) else {}
|
||||
|
||||
|
||||
def _extract_loss_logits_from_causal_lm_doc(auto_mod) -> dict[str, Any]:
|
||||
causal_cls = _get_causal_lm_output_with_past(auto_mod)
|
||||
if causal_cls is None or not getattr(causal_cls, "__doc__", None):
|
||||
return {}
|
||||
params = _parse_docstring(auto_mod, causal_cls.__doc__, max_indent_level=4)
|
||||
out: dict[str, Any] = {}
|
||||
for key in ("loss", "logits"):
|
||||
if key in params:
|
||||
out[key] = params[key]
|
||||
return out
|
||||
|
||||
|
||||
def _model_output_args_has_loss_logits(auto_mod) -> bool:
|
||||
moa = _get_model_output_args_class(auto_mod)
|
||||
if moa is None:
|
||||
return False
|
||||
loss = getattr(moa, "loss", None)
|
||||
logits = getattr(moa, "logits", None)
|
||||
if not isinstance(loss, dict) or not isinstance(logits, dict):
|
||||
return False
|
||||
return bool(loss.get("description")) and bool(logits.get("description"))
|
||||
|
||||
|
||||
def _is_model_output_args(obj: Any, auto_mod) -> bool:
|
||||
moa = _get_model_output_args_class(auto_mod)
|
||||
return moa is not None and obj is moa
|
||||
|
||||
|
||||
def _make_wrapped_get_args(
|
||||
original: Callable[..., dict],
|
||||
auto_mod,
|
||||
supplemental: dict[str, Any],
|
||||
) -> Callable[..., dict]:
|
||||
def wrapped(args_classes: object | list[object]) -> dict:
|
||||
result = dict(original(args_classes))
|
||||
if _is_model_output_args(args_classes, auto_mod):
|
||||
for key, value in supplemental.items():
|
||||
if key not in result:
|
||||
result[key] = value
|
||||
elif isinstance(args_classes, (list, tuple)):
|
||||
for cls in args_classes:
|
||||
if _is_model_output_args(cls, auto_mod):
|
||||
for key, value in supplemental.items():
|
||||
if key not in result:
|
||||
result[key] = value
|
||||
break
|
||||
return result
|
||||
|
||||
setattr(wrapped, _PATCH_TAG, True)
|
||||
wrapped._qwen_lora_loader_original = original # type: ignore[attr-defined]
|
||||
return wrapped
|
||||
|
||||
|
||||
def _qwen_modules_already_imported() -> bool:
|
||||
return _QWEN3_MODULE in sys.modules or _QWEN25_MODULE in sys.modules
|
||||
|
||||
|
||||
def _count_error_markers(text: str) -> int:
|
||||
return sum(text.count(m) for m in _ERROR_MARKERS)
|
||||
|
||||
|
||||
def _subprocess_import_probe() -> tuple[bool, str]:
|
||||
"""
|
||||
Import Qwen VL output classes in a child process without our patch.
|
||||
Returns (needs_patch, diagnostic_message).
|
||||
"""
|
||||
code = f"""
|
||||
import io, sys
|
||||
from contextlib import redirect_stdout
|
||||
buf = io.StringIO()
|
||||
try:
|
||||
with redirect_stdout(buf):
|
||||
import transformers.models.qwen3_vl.modeling_qwen3_vl as m3
|
||||
import transformers.models.qwen2_5_vl.modeling_qwen2_5_vl as m25
|
||||
_ = m3.{_QWEN3_CLASS}
|
||||
_ = m25.{_QWEN25_CLASS}
|
||||
except Exception as e:
|
||||
print("PROBE_EXCEPTION:", e, file=buf)
|
||||
out = buf.getvalue()
|
||||
markers = sum(out.count(x) for x in {list(_ERROR_MARKERS)!r})
|
||||
print("PROBE_MARKERS", markers)
|
||||
print("PROBE_LEN", len(out))
|
||||
"""
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
[sys.executable, "-c", code],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=120,
|
||||
env={k: v for k, v in os.environ.items() if k != _ENV_DISABLE},
|
||||
)
|
||||
except (subprocess.TimeoutExpired, OSError) as exc:
|
||||
return True, f"subprocess probe failed ({exc}); applying patch conservatively"
|
||||
|
||||
combined = (proc.stdout or "") + (proc.stderr or "")
|
||||
markers = 0
|
||||
for line in combined.splitlines():
|
||||
if line.startswith("PROBE_MARKERS"):
|
||||
try:
|
||||
markers = int(line.split()[-1])
|
||||
except ValueError:
|
||||
pass
|
||||
if proc.returncode != 0 and markers == 0:
|
||||
return True, f"subprocess probe exit {proc.returncode}; applying patch conservatively"
|
||||
if markers > 0:
|
||||
return True, f"subprocess probe saw {markers} loss/logits error marker(s)"
|
||||
return False, "subprocess probe: no loss/logits error markers without patch"
|
||||
|
||||
|
||||
def apply_transformers_qwen_vl_causal_lm_docstring_patch() -> bool:
|
||||
"""
|
||||
Install get_args_doc_from_source wrapper if needed.
|
||||
Returns True if patch is active (installed or already present).
|
||||
"""
|
||||
if _env_disabled():
|
||||
print(
|
||||
f"[INFO] ComfyUI-QwenImageLoraLoader: {_ENV_DISABLE} set; "
|
||||
"skipping CausalLM ModelOutput docstring patch"
|
||||
)
|
||||
return False
|
||||
|
||||
auto_mod = _get_auto_docstring_module()
|
||||
get_args = getattr(auto_mod, "get_args_doc_from_source", None)
|
||||
if get_args is None:
|
||||
print(
|
||||
"[WARNING] ComfyUI-QwenImageLoraLoader: "
|
||||
"transformers.utils.auto_docstring.get_args_doc_from_source not found; "
|
||||
"skipping docstring patch"
|
||||
)
|
||||
return False
|
||||
|
||||
if getattr(get_args, _PATCH_TAG, False):
|
||||
return True
|
||||
|
||||
if _model_output_args_has_loss_logits(auto_mod):
|
||||
print(
|
||||
"[INFO] ComfyUI-QwenImageLoraLoader: upstream ModelOutputArgs already "
|
||||
"documents loss/logits; skipping docstring patch"
|
||||
)
|
||||
return False
|
||||
|
||||
needs_patch, probe_msg = _subprocess_import_probe()
|
||||
if not needs_patch:
|
||||
print(
|
||||
f"[INFO] ComfyUI-QwenImageLoraLoader: {probe_msg}; "
|
||||
"skipping docstring patch"
|
||||
)
|
||||
return False
|
||||
|
||||
if _qwen_modules_already_imported():
|
||||
print(
|
||||
"[WARNING] ComfyUI-QwenImageLoraLoader: Qwen VL modeling modules already "
|
||||
"imported before docstring patch; restart ComfyUI for a clean apply. "
|
||||
f"({probe_msg})"
|
||||
)
|
||||
return False
|
||||
|
||||
supplemental = _extract_loss_logits_from_causal_lm_doc(auto_mod)
|
||||
if "loss" not in supplemental or "logits" not in supplemental:
|
||||
print(
|
||||
"[WARNING] ComfyUI-QwenImageLoraLoader: could not extract loss/logits "
|
||||
"from CausalLMOutputWithPast.__doc__; skipping docstring patch"
|
||||
)
|
||||
return False
|
||||
|
||||
wrapped = _make_wrapped_get_args(get_args, auto_mod, supplemental)
|
||||
auto_mod.get_args_doc_from_source = wrapped
|
||||
print(
|
||||
"[INFO] Patched transformers.utils.auto_docstring.get_args_doc_from_source "
|
||||
"for Qwen VL CausalLM ModelOutput docstrings (loss/logits); "
|
||||
"removes when upstream adds them"
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
def remove_transformers_qwen_vl_causal_lm_docstring_patch() -> bool:
|
||||
"""Restore original get_args_doc_from_source if our wrapper is installed."""
|
||||
auto_mod = _get_auto_docstring_module()
|
||||
get_args = getattr(auto_mod, "get_args_doc_from_source", None)
|
||||
if get_args is None or not getattr(get_args, _PATCH_TAG, False):
|
||||
return False
|
||||
original = getattr(get_args, "_qwen_lora_loader_original", None)
|
||||
if original is not None:
|
||||
auto_mod.get_args_doc_from_source = original
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def verify_patch_in_process() -> dict[str, Any]:
|
||||
"""Import Qwen VL output classes in-process; return error counts (for tests)."""
|
||||
auto_mod = _get_auto_docstring_module()
|
||||
get_args = getattr(auto_mod, "get_args_doc_from_source", None)
|
||||
buf = io.StringIO()
|
||||
errors_before = 0
|
||||
try:
|
||||
with redirect_stdout(buf):
|
||||
if _QWEN3_MODULE in sys.modules:
|
||||
del sys.modules[_QWEN3_MODULE]
|
||||
if _QWEN25_MODULE in sys.modules:
|
||||
del sys.modules[_QWEN25_MODULE]
|
||||
import transformers.models.qwen3_vl.modeling_qwen3_vl as m3
|
||||
import transformers.models.qwen2_5_vl.modeling_qwen2_5_vl as m25
|
||||
_ = m3.Qwen3VLCausalLMOutputWithPast
|
||||
_ = m25.Qwen2_5_VLCausalLMOutputWithPast
|
||||
except Exception as exc:
|
||||
return {
|
||||
"ok": False,
|
||||
"exception": str(exc),
|
||||
"errors": -1,
|
||||
"output": buf.getvalue(),
|
||||
"patched": bool(get_args and getattr(get_args, _PATCH_TAG, False)),
|
||||
}
|
||||
out = buf.getvalue()
|
||||
errors = _count_error_markers(out)
|
||||
return {
|
||||
"ok": errors == 0,
|
||||
"errors": errors,
|
||||
"output": out,
|
||||
"patched": bool(get_args and getattr(get_args, _PATCH_TAG, False)),
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 `prestartup_script.py` (complete)
|
||||
|
||||
```python
|
||||
"""
|
||||
ComfyUI prestartup hook for ComfyUI-QwenImageLoraLoader.
|
||||
|
||||
Runs before custom node package __init__ files are loaded so runtime patches
|
||||
apply before heavy imports (transformers Qwen VL, nunchaku, etc.).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def _patch_nunchaku_apply_rotary_emb_compat() -> None:
|
||||
"""Apply apply_rotary_emb compat patch (v2.4.6); see md/QWEN_IMAGE_APPLY_ROTARY_EMB_COMPAT_FIX.md."""
|
||||
try:
|
||||
from patches.nunchaku_patch import apply_nunchaku_patch
|
||||
|
||||
apply_nunchaku_patch()
|
||||
except Exception as exc:
|
||||
print(
|
||||
f"[WARNING] ComfyUI-QwenImageLoraLoader prestartup: "
|
||||
f"nunchaku apply_rotary_emb compat patch failed: {exc}"
|
||||
)
|
||||
|
||||
|
||||
def _patch_transformers_qwen_vl_causal_lm_docstring() -> None:
|
||||
"""Apply transformers Qwen VL CausalLM ModelOutput docstring patch (loss/logits)."""
|
||||
try:
|
||||
from patches.transformers_qwen_vl_docstring_patch import (
|
||||
apply_transformers_qwen_vl_causal_lm_docstring_patch,
|
||||
)
|
||||
|
||||
if apply_transformers_qwen_vl_causal_lm_docstring_patch():
|
||||
print(
|
||||
"[INFO] ComfyUI-QwenImageLoraLoader prestartup: "
|
||||
"CausalLM ModelOutput docstring patch applied"
|
||||
)
|
||||
except Exception as exc:
|
||||
print(
|
||||
f"[WARNING] ComfyUI-QwenImageLoraLoader prestartup: "
|
||||
f"CausalLM ModelOutput docstring patch failed: {exc}"
|
||||
)
|
||||
|
||||
|
||||
# Docstring patch first: must run before any Qwen VL modeling import.
|
||||
_patch_transformers_qwen_vl_causal_lm_docstring()
|
||||
_patch_nunchaku_apply_rotary_emb_compat()
|
||||
```
|
||||
**No environment variables.** When Hugging Face fixes `transformers`, steps 3–4 skip installation automatically on the next ComfyUI start (upstream auto-disable).
|
||||
|
||||
---
|
||||
|
||||
@@ -521,13 +194,11 @@ Supplemental entries are built once at apply time from `CausalLMOutputWithPast._
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[ComfyUI starts] --> B[prestartup_script.py]
|
||||
B --> C{Env disable?}
|
||||
C -->|yes| Z[Skip patch]
|
||||
C -->|no| D{ModelOutputArgs has loss+logits?}
|
||||
D -->|yes| Z
|
||||
B --> D{ModelOutputArgs has loss+logits?}
|
||||
D -->|yes| Z[Skip patch — upstream fixed]
|
||||
D -->|no| E[Subprocess import probe]
|
||||
E -->|no errors| Z
|
||||
E -->|errors| F{Qwen modules already imported?}
|
||||
E -->|clean stdout| Z
|
||||
E -->|errors or inconclusive| F{Qwen modules already imported?}
|
||||
F -->|yes| W[WARN: restart needed]
|
||||
F -->|no| G[Parse CausalLMOutputWithPast.__doc__]
|
||||
G --> H[Wrap get_args_doc_from_source]
|
||||
@@ -542,19 +213,18 @@ The wrapper sets `_qwen_lora_loader_causal_lm_docstring_patch = True`. A second
|
||||
|
||||
---
|
||||
|
||||
## 6. Upstream self-disable (same idea as v2.4.6 rotary patch)
|
||||
## 6. Automatic upstream disable (same idea as v2.4.6 rotary patch)
|
||||
|
||||
The patch is **temporary**. It should stop applying when Hugging Face fixes upstream or when the environment no longer needs it.
|
||||
**Fully automatic on every ComfyUI start.** Users do not set environment variables or toggles. `prestartup_script.py` runs probes; the wrapper is installed **only while upstream `transformers` still triggers `[ERROR] loss` / `[ERROR] logits`**, and **is not installed** once Hugging Face fixes `ModelOutputArgs` or a clean import probe shows zero errors.
|
||||
|
||||
| Scenario | Behavior |
|
||||
|----------|----------|
|
||||
| **A. Upstream fix** | `ModelOutputArgs` gains `loss` and `logits` with non-empty `description` → skip patch, log info |
|
||||
| **B. User disable** | `TRANSFORMERS_CAUSAL_LM_DOCSTRING_PATCH=0` (or `false` / `off` / `no` / `disable` / `disabled`) → skip patch |
|
||||
| **C. Subprocess probe** | Fresh Python imports Qwen VL classes without patch; if zero `[ERROR] loss/logits` markers → skip patch |
|
||||
| **D. Late import** | Qwen VL modules already in `sys.modules` → warn; patch not applied (restart required) |
|
||||
| **E. Missing API** | No `get_args_doc_from_source` or cannot parse parent doc → skip with warning |
|
||||
| **A. Upstream fix (schema)** | `ModelOutputArgs` has `loss` and `logits` with non-empty `description` → skip patch, log info |
|
||||
| **B. Upstream fix (probe)** | Subprocess imports Qwen VL without this patch; zero `[ERROR] loss/logits` on stdout → skip patch, log info |
|
||||
| **C. Late import** | Qwen VL modules already in `sys.modules` → warn; patch not applied until restart |
|
||||
| **D. Missing API** | No `get_args_doc_from_source` or cannot parse parent doc → skip with warning |
|
||||
|
||||
This mirrors the v2.4.6 pattern: probe upstream, patch only when needed, document removal conditions.
|
||||
When **A** or **B** applies, no wrapper is installed — the same prestartup / probe-first pattern as v2.4.6 `apply_rotary_emb` compat.
|
||||
|
||||
---
|
||||
|
||||
@@ -569,6 +239,18 @@ After a clean restart with the patch applied **before** any Qwen VL import:
|
||||
[INFO] ComfyUI-QwenImageLoraLoader prestartup: CausalLM ModelOutput docstring patch applied
|
||||
```
|
||||
|
||||
When upstream is already fixed, expect skip messages instead (no wrapper):
|
||||
|
||||
```text
|
||||
[INFO] CausalLM ModelOutput docstring patch skipped: transformers ModelOutputArgs already documents loss and logits (upstream fixed — patch not installed)
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```text
|
||||
[INFO] CausalLM ModelOutput docstring patch skipped: Qwen VL ModelOutput docstrings resolve loss/logits without docstring errors (upstream fixed — patch not installed)
|
||||
```
|
||||
|
||||
Full startup should show **zero** lines containing `[ERROR] \`loss\`` or `[ERROR] \`logits\`` for the Qwen VL output classes.
|
||||
|
||||
### 7.2 In-process check (optional)
|
||||
@@ -576,18 +258,10 @@ Full startup should show **zero** lines containing `[ERROR] \`loss\`` or `[ERROR
|
||||
From the extension root, with ComfyUI’s Python:
|
||||
|
||||
```bat
|
||||
cmd /c "cd /d D:\USERFILES\ComfyUI\ComfyUI\custom_nodes\ComfyUI-QwenImageLoraLoader && D:\USERFILES\ComfyUI\python_embeded\python.exe -c \"from patches.transformers_qwen_vl_docstring_patch import apply_transformers_qwen_vl_causal_lm_docstring_patch, verify_patch_in_process; apply_transformers_qwen_vl_causal_lm_docstring_patch(); print(verify_patch_in_process())\""
|
||||
cmd /c "cd /d D:\USERFILES\ComfyUI\ComfyUI\custom_nodes\ComfyUI-QwenImageLoraLoader && D:\USERFILES\ComfyUI\python_embeded\python.exe _verify_docstring_patch.py"
|
||||
```
|
||||
|
||||
Expected: `'ok': True`, `'errors': 0`, `'patched': True`.
|
||||
|
||||
### 7.3 Disable probe
|
||||
|
||||
```bat
|
||||
set TRANSFORMERS_CAUSAL_LM_DOCSTRING_PATCH=0
|
||||
```
|
||||
|
||||
Restart ComfyUI: patch skip message should appear; if upstream is still broken, `[ERROR] loss/logits` may return.
|
||||
Expected when upstream is still broken: patch applies and Qwen VL imports report zero `[ERROR] loss/logits` markers.
|
||||
|
||||
---
|
||||
|
||||
@@ -599,7 +273,7 @@ Restart ComfyUI: patch skip message should appear; if upstream is still broken,
|
||||
| **Cause** | `ModelOutputArgs` lacks `loss`/`logits`; parent `Args:` not parsed at indent 0 |
|
||||
| **Fix** | Wrap `get_args_doc_from_source` in custom node `prestartup_script.py` |
|
||||
| **Scope** | ComfyUI-QwenImageLoraLoader only; no `site-packages` changes |
|
||||
| **Self-disable** | Upstream fix, env var, subprocess probe, idempotent wrapper |
|
||||
| **Auto-disable** | Upstream `ModelOutputArgs` fix and/or clean subprocess import probe; no user env vars |
|
||||
| **Related** | [v2.4.6 apply_rotary_emb compat](https://github.com/ussoewwin/ComfyUI-QwenImageLoraLoader/releases/tag/v2.4.6) — same prestartup / self-disable pattern |
|
||||
|
||||
---
|
||||
|
||||
@@ -5,14 +5,14 @@ When Hugging Face transformers merges CausalLMOutputWithPast docs into Qwen VL
|
||||
ModelOutput subclasses, validation falls back to ModelOutputArgs — which lacks
|
||||
loss/logits — and prints [ERROR] at import time.
|
||||
|
||||
Upstream self-disable (same pattern as v2.4.6 apply_rotary_emb compat):
|
||||
Fully automatic at ComfyUI prestartup (no user env vars):
|
||||
- Probe upstream ModelOutputArgs before installing any wrapper.
|
||||
- If upstream already documents loss/logits, skip (no wrapper, harmless).
|
||||
- If import probe shows clean stdout, skip.
|
||||
- Disable via TRANSFORMERS_CAUSAL_LM_DOCSTRING_PATCH=0|false|off|no|disable|disabled
|
||||
- If upstream already documents loss/logits, skip (no wrapper).
|
||||
- If a subprocess import probe shows clean stdout, skip.
|
||||
- Otherwise install the wrapper (default when upstream is still broken).
|
||||
|
||||
Fix: wrap get_args_doc_from_source to merge loss/logits into the returned dict
|
||||
only while upstream ModelOutputArgs still omits those fields.
|
||||
while upstream ModelOutputArgs still omits those fields.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -38,11 +38,6 @@ _patch_applied: bool = False
|
||||
_original_get_args_doc_from_source: Optional[Callable[..., dict]] = None
|
||||
|
||||
|
||||
def _patch_disabled_by_env() -> bool:
|
||||
value = os.environ.get("TRANSFORMERS_CAUSAL_LM_DOCSTRING_PATCH", "").strip().lower()
|
||||
return value in ("0", "false", "no", "off", "disable", "disabled")
|
||||
|
||||
|
||||
def _qwen_vl_modeling_already_imported() -> bool:
|
||||
return any(name in sys.modules for name in _QWEN_VL_MODELING_MODULES)
|
||||
|
||||
@@ -155,7 +150,6 @@ def _import_probe_reports_clean() -> Optional[bool]:
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=120,
|
||||
env={**os.environ, "TRANSFORMERS_CAUSAL_LM_DOCSTRING_PATCH": "0"},
|
||||
)
|
||||
except (OSError, subprocess.SubprocessError) as exc:
|
||||
logger.debug("CausalLM docstring import probe failed: %s", exc)
|
||||
@@ -191,13 +185,6 @@ def apply_transformers_causal_lm_docstring_patch() -> bool:
|
||||
if _patch_applied:
|
||||
return True
|
||||
|
||||
if _patch_disabled_by_env():
|
||||
logger.info(
|
||||
"CausalLM ModelOutput docstring patch skipped: "
|
||||
"TRANSFORMERS_CAUSAL_LM_DOCSTRING_PATCH is disabled"
|
||||
)
|
||||
return False
|
||||
|
||||
if _qwen_vl_modeling_already_imported():
|
||||
logger.warning(
|
||||
"CausalLM ModelOutput docstring patch skipped: Qwen VL modeling modules "
|
||||
|
||||
Reference in New Issue
Block a user