Skip to content

[Issue 101][eval-and-fix] Audit forward-path device and dtype casts (closes pollockjj/mydevelopment#185)#49

Merged
pollockjj merged 4 commits into
issue_101from
issue_185
May 6, 2026
Merged

[Issue 101][eval-and-fix] Audit forward-path device and dtype casts (closes pollockjj/mydevelopment#185)#49
pollockjj merged 4 commits into
issue_101from
issue_185

Conversation

@pollockjj
Copy link
Copy Markdown
Owner

Plan: Remove residual comfy.model_management.get_torch_device() cast from MMModule.forward

Overview

comfy/ldm/seedvr/model.py on pollockjj/ComfyUI:origin/issue_101 HEAD 98df2309 carries one residual forward-path device lookup that fights Comfy's model-management system: MMModule.forward (file lines 613–631) reads device = comfy.model_management.get_torch_device() at line 624 and forces the input via vid = vid.to(device) at line 625 — the exact pattern Kosinkadink flagged on Comfy-Org#11294 (issue-comment 4259303182, item 4) and the only one of Kosinkadink's five flagged sites that survived Yousef's Fixed 3, 4 reply. Phase 0 ground-truth probes (GTP-2, GTP-3) confirm one get_torch_device() call in any forward method body across the file (MMModule.forward, line 624). The slice removes those two lines, locks the repository invariant via an AST-walked regression that asserts zero get_torch_device() calls inside any forward method body in this file, and adds a behavioral smoke test that monkey-patches comfy.model_management.get_torch_device to raise — proving the post-fix forward never looks it up — while exercising MMModule.forward(vid, txt) end-to-end against an nn.Identity-shaped stub on CPU. Companion GTP-3 reports one .to(device=..., dtype=...) keyword cast at line 629; that cast is a derived-device cast (device=vid.device) sourced from the input stream, not from a Comfy lookup, and is left untouched per the issue body's specific scope to "lines 624-625" (Out of Scope §). The fix lands on pollockjj/ComfyUI:issue_185 cut from origin/issue_101 HEAD 98df2309 and PRs back into issue_101, mirroring the merged sequence #46 / #44 / #43 / #36 / #35 / #34 / #33 / #32 / #31.

Diagnosis Summary

The buggy method body, captured live from origin/issue_101:comfy/ldm/seedvr/model.py lines 613–631 (Ground Truth Probe GTP-4):

def forward(
    self,
    vid: torch.FloatTensor,
    txt: torch.FloatTensor,
    *args,
    **kwargs,
) -> Tuple[
    torch.FloatTensor,
    torch.FloatTensor,
]:
    vid_module = self.vid if not self.shared_weights else self.all
    device = comfy.model_management.get_torch_device()
    vid = vid.to(device)
    vid = vid_module(vid, *get_args("vid", args), **get_kwargs("vid", kwargs))
    if not self.vid_only:
        txt_module = self.txt if not self.shared_weights else self.all
        txt = txt.to(device=vid.device, dtype=vid.dtype)
        txt = txt_module(txt, *get_args("txt", args), **get_kwargs("txt", kwargs))
    return vid, txt

Lines 624 (device = comfy.model_management.get_torch_device()) and 625 (vid = vid.to(device)) are the residual cast. Comfy's model-management system places weights and inputs on load_device before forward is invoked; an inner module re-querying the global get_torch_device() and forcing the input there bypasses Comfy's per-model load_device decision (which can differ from the global probe under partial offload, low-VRAM, or CPU-fallback configurations). Removing both lines lets vid ride whatever device Comfy passed in, which is the Comfy-conventional contract.

The method-local txt = txt.to(device=vid.device, dtype=vid.dtype) at line 629 is structurally distinct: its device target is vid.device (derived from the input stream, post-vid_module call), not comfy.model_management.get_torch_device(). Phase 0 reports it for completeness but the issue body explicitly scopes the deliverable to "the remaining cast at MMModule.forward (lines 624-625)" — Kosinkadink's get_torch_device()+.to(device) pattern. Line 629 also carries a dtype=vid.dtype component whose load-bearing role for cross-stream dtype alignment is not in scope for this audit (Out of Scope §).

Reproduction command: `cd /home/johnj/dev_cuda_1/ComfyUI && python3 -m pytest tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py -v`
Observed failure pre-fix: `test_no_get_torch_device_in_forward_methods` reports one match at MMModule.forward (lineno=613, call.lineno=624); `test_mmmodule_forward_succeeds_without_get_torch_device_lookup` raises `RuntimeError: MMModule.forward called get_torch_device()` from inside the patched lookup. Both flip to PASS on the post-fix branch.

Affected Repositories

Repository Path Base Branch Issue Branch
pollockjj/ComfyUI /home/johnj/dev_cuda_1/ComfyUI issue_101 (HEAD 98df2309) issue_185
pollockjj/mydevelopment /home/johnj/dev_cuda_1/mydevelopment main issue_185

pollockjj/ComfyUI is the deliverable: the edit to comfy/ldm/seedvr/model.py (MMModule.forward, lines 624–625 removed) and the new regression test under tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py land on issue_185 cut from origin/issue_101 HEAD 98df2309 and PR back into issue_101. This mirrors the merged eval-and-fix PR sequence #46 (Comfy-Org#192), #44 (Comfy-Org#190), #43 (Comfy-Org#194), #36 (Comfy-Org#119), #35 (Comfy-Org#188), #34 (Comfy-Org#189), #33 (#109), #32 (Comfy-Org#183), #31 (groupnorm).

pollockjj/mydevelopment is the bookkeeping target: every committed artifact under github_issues/185/slice1/ (Phase 0 probe transcript, A/B pytest logs, AST-extracted forward source, scope-bounded diff, test source copy) lives on the outer workspace's issue_185 branch and PRs back into main as the standard [bookkeeping] [Issue 101][eval-and-fix] pattern.

Asset Readiness

Asset Location Provenance Status Evidence
ComfyUI deliverable checkout /home/johnj/dev_cuda_1/ComfyUI pollockjj/ComfyUI branch issue_101 -> issue_185 VERIFIED branch verified; git rev-parse origin/issue_101 = 98df2309
Python test environment /home/johnj/dev_cuda_1/ComfyUI/.venv/ local-only slot venv, torch 2.11.0+cu130, CUDA available VERIFIED GTP-7, GTP-8
Base-branch source under audit origin/issue_101:comfy/ldm/seedvr/model.py pollockjj/ComfyUI origin/issue_101 (HEAD 98df2309) VERIFIED GTP-1, GTP-2, GTP-3, GTP-4, GTP-6
comfy.model_management.get_torch_device import target comfy/model_management.py upstream Comfy-Org master, present on slot venv VERIFIED GTP-7
MMModule / MMArg / get_args / get_kwargs symbol contracts origin/issue_101:comfy/ldm/seedvr/model.py pollockjj/ComfyUI origin/issue_101 (lines 154–156, 177–178, 181–182, 589–631) VERIFIED GTP-6
Existing SeedVR2 test surface (must remain green) origin/issue_101:tests-unit/comfy_test/ + tests-unit/comfy_extras_test/ pollockjj/ComfyUI origin/issue_101 (12 files) VERIFIED GTP-5
Bookkeeping workspace /home/johnj/dev_cuda_1/mydevelopment pollockjj/mydevelopment main -> issue_185 VERIFIED artifact paths under github_issues/185/slice1/ writable on issue_185

Research and Methodology

Plan Foundations Comment URL: https://github.com/pollockjj/mydevelopment/issues/185#issuecomment-4383764590 — Foundations comment for Comfy-Org#185 is the load-bearing reference for the inline R&M and TPM content reproduced verbatim below; every AC carrying a Foundations-pin: line cites the comment node-id and createdAt timestamp (lastEditedAt is null on this comment, so qa-plan Check 21 sub-check 6 falls back to createdAt as the authoritative pin).

Detected Scope: none — port of the established eval-and-fix pattern to one residual cast site flagged by Kosinkadink on the upstream PR review (Comfy-Org#11294 issue-comment 4259303182, item 4); existing Comfy model-management contract IS the equivalence reference, not a published metric. Equivalence rule is binary source-AST equality (zero get_torch_device() Call nodes inside any forward FunctionDef body in comfy/ldm/seedvr/model.py) plus binary behavioral equality (post-fix MMModule.forward(vid, txt) runs to completion with comfy.model_management.get_torch_device monkey-patched to raise, returning tensors on the same device as the inputs). No tolerance band, no statistical test, no measurement instrument.

The change class is "honor an existing internal API contract" — Comfy's per-model load_device placement contract for forward invocations. Source authority is the live source of comfy/ldm/seedvr/model.py on the issue's base branch (origin/issue_101 HEAD 98df2309), captured verbatim under ## Ground Truth Probes. Binding parent-issue directives quoted verbatim from pollockjj/mydevelopment#185 issue body:

"Either remove this cast (evidence-backed) or retain it with a regression that proves the load-bearing role."

"Code change in comfy/ldm/seedvr/model.py resolving the remaining cast at MMModule.forward (lines 624-625) — either removed or annotated with the evidence that justifies retention."

"Regression test exercising the audit invariant (no comfy.model_management.get_torch_device() call inside any forward method in comfy/ldm/seedvr/model.py) and the load-bearing question."

"A normal Comfy model-loading + forward-pass smoke through MMModule succeeds on the post-fix branch under standard load_device placement — the existing seedvr_model_test.py harness is the precedent for stubbing this without real SeedVR2 weights."

"Existing SeedVR2 tests on issue_101 (test_seedvr_groupnorm_limit.py, test_seedvr_rope_delegation.py, seedvr_model_test.py, plus the seven test_seedvr_vae_* files) remain bit-identical pass."

"Changes to comfy/ldm/seedvr/vae.py, comfy_extras/nodes_seedvr.py, or comfy/sd.py" are out of scope.

"Sister issue #110 also touches comfy/ldm/seedvr/model.py but at different sites (structure-resolution helper, RoPE cache); coordinate at merge if both land same day."

"Upstream propagation to Comfy-Org#11294 (separate later decision)" is out of scope.

These directives bound every AC below. Diff scope is confined to MMModule.forward body (lines 613–631 on the origin/issue_101 side); the parent class structure-resolution helper and RoPE cache touched by #110 are off-limits; upstream PR Comfy-Org#11294 is not perturbed.

Tools, Pipeline, and Measurements

Plan Foundations Comment URL: https://github.com/pollockjj/mydevelopment/issues/185#issuecomment-4383764590 — same Foundations comment as the R&M section above; the TPM content reproduced verbatim below is the load-bearing reference for tool / version / pipeline citations in every AC carrying a Foundations-pin: line.

Trivial scope; no external tools beyond the change itself.

Tool / Dep / Repo Version / SHA Status Citation
pytest repo tests-unit/requirements.txt pin (per tests-unit/README.md) REUSE sister tests test_seedvr_groupnorm_limit.py, seedvr_model_test.py exercise the same venv on origin/issue_101
torch 2.11.0+cu130 on /home/johnj/dev_cuda_1/ComfyUI/.venv/ REUSE GTP-8
comfy.model_management.get_torch_device comfy/model_management.py import path on slot venv REUSE GTP-7
comfy.ldm.seedvr.model.MMModule / MMArg / get_args / get_kwargs origin/issue_101 (lines 154–156, 177–178, 181–182, 589–631) REUSE GTP-6
git system REUSE for diff/log/show/checkout/restore artifacts
stdlib ast, inspect, re, unittest.mock (via monkeypatch fixture) Python interpreter on .venv/ REUSE for source-shape introspection (mirrors seedvr_model_test.py AST pattern)

Pipeline: edit one method body in one source file (comfy/ldm/seedvr/model.py, MMModule.forward, lines 624–625 on the origin/issue_101 side, removing the two lines verbatim) → add one new test file tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py defining two pytest test functions → run pytest against the new test on issue_185 HEAD (post-fix capture) and against the same test with comfy/ldm/seedvr/model.py reverted to origin/issue_101 (pre-fix capture) → commit pytest logs, AST-extracted forward source, grep transcripts, and scope-bounded git diff to github_issues/185/slice1/ on the bookkeeping branch.

Measurements: zero numeric measurements. The two evidence primitives are (a) source-AST count of get_torch_device() Call nodes inside forward FunctionDef bodies (binary, must equal 0 post-fix) and (b) behavioral pytest pass/fail on a forward invocation with get_torch_device monkey-patched to raise (binary, must PASS post-fix). HR-06 (single-probe-before-sweep) and HR-11 (boundary-bracketing) do not apply — no sweep, no metric distribution.

Ground Truth Probes

Every literal API surface the ACs below cite is anchored to a probe captured live against the issue's read target (pollockjj/ComfyUI:origin/issue_101 HEAD 98df2309) or against the active PyTorch interpreter at plan-authoring time (/home/johnj/dev_cuda_1/ComfyUI/.venv/bin/python, torch 2.11.0+cu130, CUDA available).

Probe ID Consumed by AC(s) Probe command Observed output (verbatim)
GTP-1 Slice 1 AC-3, AC-4, AC-5 cd /home/johnj/dev_cuda_1/ComfyUI && git show origin/issue_101:comfy/ldm/seedvr/model.py | python3 -c 'import ast,sys; tree=ast.parse(sys.stdin.read()); [print(f"forward {n.lineno}-{n.end_lineno}") for n in ast.walk(tree) if isinstance(n,ast.FunctionDef) and n.name=="forward"]' forward 34-51 forward 369-399 forward 422-439 forward 511-548 forward 613-631 forward 686-687 forward 731-828 forward 842-846 forward 864-865 forward 932-969 forward 984-993 forward 996-1018 forward 1033-1043 forward 1046-1064 forward 1095-1145 forward 1167-1189 forward 1387-1471 — 17 forward FunctionDefs total; the MMModule.forward body the AC operates on spans lines 613–631.
GTP-2 Slice 1 AC-1, AC-2, AC-3, AC-4 cd /home/johnj/dev_cuda_1/ComfyUI && git show origin/issue_101:comfy/ldm/seedvr/model.py | python3 -c 'import ast,sys; t=ast.parse(sys.stdin.read()); cls={id(m):c.name for c in ast.walk(t) if isinstance(c,ast.ClassDef) for m in c.body if isinstance(m,ast.FunctionDef)}; [print(f"class={cls.get(id(n))} forward.lineno={n.lineno} call.lineno={i.lineno}") for n in ast.walk(t) if isinstance(n,ast.FunctionDef) and n.name=="forward" for i in ast.walk(n) if isinstance(i,ast.Call) and isinstance(i.func,ast.Attribute) and i.func.attr=="get_torch_device"]' class=MMModule forward.lineno=613 call.lineno=624 — exactly one get_torch_device() Call node inside any forward method body in the entire file, located inside MMModule.forward at line 624. The post-fix expectation is zero matches.
GTP-3 Slice 1 AC-3 (note), Out of Scope § cd /home/johnj/dev_cuda_1/ComfyUI && git show origin/issue_101:comfy/ldm/seedvr/model.py | python3 -c 'import ast,sys; t=ast.parse(sys.stdin.read()); cls={id(m):c.name for c in ast.walk(t) if isinstance(c,ast.ClassDef) for m in c.body if isinstance(m,ast.FunctionDef)}; [print(f"class={cls.get(id(n))} forward.lineno={n.lineno} call.lineno={i.lineno}") for n in ast.walk(t) if isinstance(n,ast.FunctionDef) and n.name=="forward" for i in ast.walk(n) if isinstance(i,ast.Call) and isinstance(i.func,ast.Attribute) and i.func.attr=="to" and any(kw.arg=="device" for kw in i.keywords)]' class=MMModule forward.lineno=613 call.lineno=629 — exactly one .to(device=...) keyword cast inside any forward method body, located at MMModule.forward line 629 (txt = txt.to(device=vid.device, dtype=vid.dtype)). Its device argument derives from vid.device (input-stream device), NOT from comfy.model_management.get_torch_device(); this is reported by the Phase 0 probe as a structurally distinct pattern from the lines 624-625 deliverable scope (Out of Scope §).
GTP-4 Slice 1 AC-1, AC-2, AC-3, AC-4 cd /home/johnj/dev_cuda_1/ComfyUI && git show origin/issue_101:comfy/ldm/seedvr/model.py | sed -n '613,631p'
    def forward(
self,
vid: torch.FloatTensor,
txt: torch.FloatTensor,
*args,
**kwargs,
) -> Tuple[
torch.FloatTensor,
torch.FloatTensor,
]:
vid_module = self.vid if not self.shared_weights else self.all
device = comfy.model_management.get_torch_device()
vid = vid.to(device)
vid = vid_module(vid, *get_args("vid", args), **get_kwargs("vid", kwargs))
if not self.vid_only:
txt_module = self.txt if not self.shared_weights else self.all
txt = txt.to(device=vid.device, dtype=vid.dtype)
txt = txt_module(txt, *get_args("txt", args), **get_kwargs("txt", kwargs))
return vid, txt
The two lines to remove are 624 (device = comfy.model_management.get_torch_device()) and 625 (vid = vid.to(device)); every other line of the body is preserved verbatim.
GTP-5 Slice 1 AC-6 cd /home/johnj/dev_cuda_1/ComfyUI && git ls-tree --name-only origin/issue_101 tests-unit/comfy_test/ tests-unit/comfy_extras_test/ | grep -E 'seedvr'
tests-unit/comfy_extras_test/test_seedvr_conditioning_hardening.py
tests-unit/comfy_extras_test/test_seedvr_node_signature.py
tests-unit/comfy_test/seedvr_model_test.py
tests-unit/comfy_test/seedvr_vae_forward_test.py
tests-unit/comfy_test/seedvr_vae_wrapper_forward_test.py
tests-unit/comfy_test/test_seedvr_groupnorm_limit.py
tests-unit/comfy_test/test_seedvr_rope_delegation.py
tests-unit/comfy_test/test_seedvr_vae_decode_batch_axes.py
tests-unit/comfy_test/test_seedvr_vae_decode_guards.py
tests-unit/comfy_test/test_seedvr_vae_decode_unpadded_t.py
tests-unit/comfy_test/test_seedvr_vae_loader_metadata.py
tests-unit/comfy_test/test_seedvr_vae_tiled_args_no_mutate.py
The 12 existing SeedVR2 tests on origin/issue_101 HEAD; AC-6 enumerates them all and asserts each remains green on issue_185 HEAD. The new file test_seedvr_forward_no_device_cast.py does not yet exist.
GTP-6 Slice 1 AC-1, AC-5 cd /home/johnj/dev_cuda_1/ComfyUI && git show origin/issue_101:comfy/ldm/seedvr/model.py | python3 -c 'import ast,sys; t=ast.parse(sys.stdin.read()); want={"MMArg","get_args","get_kwargs","MMModule"}; [print(f"{n.name}: kind={type(n).__name__} lineno={n.lineno} end={n.end_lineno}") for n in ast.walk(t) if (isinstance(n,ast.ClassDef) or isinstance(n,ast.FunctionDef)) and n.name in want]' MMArg: kind=ClassDef lineno=154 end=156 get_args: kind=FunctionDef lineno=177 end=178 get_kwargs: kind=FunctionDef lineno=181 end=182 MMModule: kind=ClassDef lineno=589 end=631 — the four symbols the smoke test scaffold imports/instantiates exist at the cited line ranges; MMModule.__init__ accepts module: Callable[..., nn.Module], *args, shared_weights: bool=False, vid_only: bool=False, **kwargs and instantiates self.vid / self.txt via module(*get_args("vid", args), **get_kwargs("vid", kwargs)).
GTP-7 Slice 1 AC-1, AC-2 cd /home/johnj/dev_cuda_1/ComfyUI && /home/johnj/dev_cuda_1/ComfyUI/.venv/bin/python -c 'import comfy.model_management as m, inspect; print("callable=",callable(m.get_torch_device)); print("sig=",inspect.signature(m.get_torch_device))' callable= True sig= ()comfy.model_management.get_torch_device is a zero-arg callable on the slot venv. The smoke test monkey-patches it via monkeypatch.setattr(comfy.model_management, "get_torch_device", boom) where boom is a zero-arg function that increments a counter and raises RuntimeError; pre-fix, the device = comfy.model_management.get_torch_device() lookup at MMModule.forward line 624 invokes boom, the counter increments, and RuntimeError propagates out of forward. Post-fix, the lookup is gone and the counter remains zero.
GTP-8 Slice 1 AC-1, AC-2 cd /home/johnj/dev_cuda_1/ComfyUI && /home/johnj/dev_cuda_1/ComfyUI/.venv/bin/python -c 'import torch; print("torch", torch.__version__); print("cuda_available", torch.cuda.is_available())' torch 2.11.0+cu130 cuda_available True — slot venv is CUDA-capable; on a CUDA-host run, an unpatched get_torch_device() returns a CUDA device, so the pre-fix vid.to(device) cast at line 625 actually moves CPU input tensors to GPU. The smoke test pins inputs to torch.device("cpu") and the monkey-patched get_torch_device raises before any device move can occur, so the pre-fix failure mode is the RuntimeError from boom (not a CUDA-vs-CPU device-mismatch downstream).

If any literal in an AC is not anchored above, the AC is REJECTED. The slicer must replicate the exact GTP commands (byte-identical) when assembling the slice — every cited file:line, function name, class name, and Python literal in the slice's commit must match the GTP output above.

Created Surface Contract

The plan introduces the following literals; they are anchored by the slice that creates them, not by Phase 0 probes (these strings do not exist on the read target — the slicer writes them):

Surface Created by Contract
tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py Slice 1 New file in pollockjj/ComfyUI on issue_185. Defines exactly two pytest test functions: (1) test_no_get_torch_device_in_forward_methods — uses inspect.getsource(comfy.ldm.seedvr.model) then ast.parse(...) and asserts that the list [(n.lineno, i.lineno) for n in ast.walk(tree) if isinstance(n, ast.FunctionDef) and n.name == "forward" for i in ast.walk(n) if isinstance(i, ast.Call) and isinstance(i.func, ast.Attribute) and i.func.attr == "get_torch_device"] is empty. (2) test_mmmodule_forward_succeeds_without_get_torch_device_lookup — uses the monkeypatch fixture to replace comfy.model_management.get_torch_device with a zero-arg callable that increments call_count[0] and raises RuntimeError("MMModule.forward called get_torch_device()"); constructs MMModule(_IdentityCallable, shared_weights=False, vid_only=False) where _IdentityCallable is a local nn.Module subclass whose forward(x, *args, **kwargs) returns x; calls mm.forward(torch.zeros(2, 4), torch.ones(2, 4)); asserts (a) call_count[0] == 0, (b) torch.equal(vid_out, vid_in), (c) torch.equal(txt_out, txt_in), (d) vid_out.device == vid_in.device, (e) txt_out.device == txt_in.device. The test sets comfy.cli_args.args.cpu = True when not torch.cuda.is_available() (matches the test_seedvr_groupnorm_limit.py import preamble).
github_issues/185/slice1/phase0_probe.log Slice 1 Committed to pollockjj/mydevelopment issue_185. Verbatim stdout of the two AST probes (GTP-2 and GTP-3) captured against origin/issue_101 HEAD 98df2309 before any slice edits. Final file content matches GTP-2's class=MMModule forward.lineno=613 call.lineno=624 line and GTP-3's class=MMModule forward.lineno=613 call.lineno=629 line, with a header recording the read-target SHA.
github_issues/185/slice1/pytest_post_fix.log Slice 1 Committed to pollockjj/mydevelopment issue_185. Verbatim stdout/stderr of cd /home/johnj/dev_cuda_1/ComfyUI && python3 -m pytest tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py -v run on issue_185 HEAD. Final non-blank summary line ends with 2 passed.
github_issues/185/slice1/pytest_pre_fix.log Slice 1 Committed to pollockjj/mydevelopment issue_185. Verbatim stdout/stderr of the same pytest invocation captured after cd /home/johnj/dev_cuda_1/ComfyUI && git checkout origin/issue_101 -- comfy/ldm/seedvr/model.py is applied to the deliverable working tree (the new test file from issue_185 HEAD remains in place; only model.py is reverted). Captures one assert not found failure on test_no_get_torch_device_in_forward_methods reporting [(613, 624)] plus one RuntimeError: MMModule.forward called get_torch_device() failure on test_mmmodule_forward_succeeds_without_get_torch_device_lookup. The deliverable working tree is restored to issue_185 HEAD via cd /home/johnj/dev_cuda_1/ComfyUI && git restore --source=HEAD comfy/ldm/seedvr/model.py immediately after capture, before any commit on the deliverable.
github_issues/185/slice1/forward_source.txt Slice 1 Committed to pollockjj/mydevelopment issue_185. Verbatim AST-extracted source of MMModule.forward from issue_185 HEAD comfy/ldm/seedvr/model.py, produced by cd /home/johnj/dev_cuda_1/ComfyUI && python3 -c 'import ast; src=open("comfy/ldm/seedvr/model.py").read(); tree=ast.parse(src); cls=[n for n in ast.walk(tree) if isinstance(n,ast.ClassDef) and n.name=="MMModule"][0]; m=[n for n in cls.body if isinstance(n,ast.FunctionDef) and n.name=="forward"][0]; print(ast.get_source_segment(src,m))'. The file content shows zero occurrences of the substrings comfy.model_management.get_torch_device and vid.to(device).
github_issues/185/slice1/forbidden_call_grep.log Slice 1 Committed to pollockjj/mydevelopment issue_185. Output of grep -n -E 'comfy\.model_management\.get_torch_device|vid = vid\.to\(device\)' github_issues/185/slice1/forward_source.txt; echo "exit=$?". The committed log records exit=1 and zero matching lines.
github_issues/185/slice1/scope_diff.txt Slice 1 Committed to pollockjj/mydevelopment issue_185. Output of cd /home/johnj/dev_cuda_1/ComfyUI && git diff origin/issue_101..issue_185 -- comfy/ldm/seedvr/model.py. The diff shows two deleted lines (the verbatim text of original lines 624 and 625 from GTP-4) and zero hunks outside the body of MMModule.forward (lines 613–631 on the origin/issue_101 side). The artifact contains the verbatim diff plus a final stanza listing every @@-hunk header range and confirming each falls inside [613, 631] on the - side.
github_issues/185/slice1/test_source.txt Slice 1 Committed to pollockjj/mydevelopment issue_185. Verbatim copy produced by cp /home/johnj/dev_cuda_1/ComfyUI/tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py github_issues/185/slice1/test_source.txt. AC-5 verifies the file defines exactly the two named test functions, the AST assertion shape, and the monkeypatch boom-counter assertion shape.
github_issues/185/slice1/companion_pytest.log Slice 1 Committed to pollockjj/mydevelopment issue_185. Verbatim stdout/stderr of cd /home/johnj/dev_cuda_1/ComfyUI && python3 -m pytest tests-unit/comfy_test/seedvr_model_test.py tests-unit/comfy_test/seedvr_vae_forward_test.py tests-unit/comfy_test/seedvr_vae_wrapper_forward_test.py tests-unit/comfy_test/test_seedvr_groupnorm_limit.py tests-unit/comfy_test/test_seedvr_rope_delegation.py tests-unit/comfy_test/test_seedvr_vae_decode_batch_axes.py tests-unit/comfy_test/test_seedvr_vae_decode_guards.py tests-unit/comfy_test/test_seedvr_vae_decode_unpadded_t.py tests-unit/comfy_test/test_seedvr_vae_loader_metadata.py tests-unit/comfy_test/test_seedvr_vae_tiled_args_no_mutate.py tests-unit/comfy_extras_test/test_seedvr_conditioning_hardening.py tests-unit/comfy_extras_test/test_seedvr_node_signature.py -v run on issue_185 HEAD. Final non-blank summary line ends with passed and zero failed / zero errors. The 12 file paths enumerated in the command match GTP-5 verbatim.

Slices


Slice 1: Remove residual get_torch_device() cast from MMModule.forward + lock invariant + smoke test

Kind: implementation

Objective: Prove that MMModule.forward() no longer reads comfy.model_management.get_torch_device() nor forces vid = vid.to(device), that the change is scoped strictly to the body of MMModule.forward (no other method body, no other class touched), that the repository-level invariant against get_torch_device() calls inside any forward method body in comfy/ldm/seedvr/model.py is locked in by an AST-walked regression, that an nn.Identity-shaped smoke through MMModule.forward(vid, txt) succeeds without invoking get_torch_device (verified via monkeypatch that raises if invoked) and preserves input device placement, and that the existing 12-test SeedVR2 surface remains pytest-green on issue_185 HEAD after the edit lands.

Acceptance Criteria

  • AC-1: cd /home/johnj/dev_cuda_1/ComfyUI && python3 -m pytest tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py -v exits 0 with 2 passed in the summary line — verified by committed artifact github_issues/185/slice1/pytest_post_fix.log whose last non-blank line ends with 2 passed.
    • Pre-fix-fingerprint: §Diagnosis Summary — same pytest invocation against origin/issue_101 source raises assert not found on test_no_get_torch_device_in_forward_methods (reporting [(613, 624)] per GTP-2) and RuntimeError: MMModule.forward called get_torch_device() on test_mmmodule_forward_succeeds_without_get_torch_device_lookup (per GTP-4 line 624 and GTP-7's boom semantics)
    • Post-fix-expectation: zero exceptions; pytest exits 0; summary line contains 2 passed
    • Assumes-passed: none
    • In-scope-of: Slice 1 Objective "Prove that MMModule.forward() no longer reads comfy.model_management.get_torch_device() nor forces vid = vid.to(device), that the change is scoped strictly to the body of MMModule.forward (no other method body, no other class touched), that the repository-level invariant against get_torch_device() calls inside any forward method body in comfy/ldm/seedvr/model.py is locked in by an AST-walked regression, that an nn.Identity-shaped smoke through MMModule.forward(vid, txt) succeeds without invoking get_torch_device (verified via monkeypatch that raises if invoked) and preserves input device placement, and that the existing 12-test SeedVR2 surface remains pytest-green on issue_185 HEAD after the edit lands."
    • Probe: GTP-1, GTP-2, GTP-4, GTP-6, GTP-7, GTP-8
    • Foundations-pin: comment-id=IC_kwDOR2e1q88AAAABBUrwbg edited-at=2026-05-05T22:59:36Z
  • AC-2: The committed A/B pytest pair for tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py -v proves the same observables flip across the fix: github_issues/185/slice1/pytest_pre_fix.log records the assert not found failure on test_no_get_torch_device_in_forward_methods (reporting [(613, 624)]) plus the RuntimeError: MMModule.forward called get_torch_device() failure on test_mmmodule_forward_succeeds_without_get_torch_device_lookup after cd /home/johnj/dev_cuda_1/ComfyUI && git checkout origin/issue_101 -- comfy/ldm/seedvr/model.py, and github_issues/185/slice1/pytest_post_fix.log records pytest exit 0 with 2 passed after cd /home/johnj/dev_cuda_1/ComfyUI && git restore --source=HEAD comfy/ldm/seedvr/model.py restores issue_185 HEAD.
    • Pre-fix-fingerprint: §Diagnosis Summary — GTP-2 records the call site at MMModule.forward line 624; GTP-4 records the verbatim two-line residual cast; GTP-7 confirms comfy.model_management.get_torch_device is the zero-arg callable that the monkey-patched boom replaces.
    • Post-fix-expectation: the same pytest invocation on restored issue_185 HEAD exits 0 with 2 passed and zero assert not found / RuntimeError failures.
    • Assumes-passed: none
    • In-scope-of: Slice 1 Objective "Prove that MMModule.forward() no longer reads comfy.model_management.get_torch_device() nor forces vid = vid.to(device), that the change is scoped strictly to the body of MMModule.forward (no other method body, no other class touched), that the repository-level invariant against get_torch_device() calls inside any forward method body in comfy/ldm/seedvr/model.py is locked in by an AST-walked regression, that an nn.Identity-shaped smoke through MMModule.forward(vid, txt) succeeds without invoking get_torch_device (verified via monkeypatch that raises if invoked) and preserves input device placement, and that the existing 12-test SeedVR2 surface remains pytest-green on issue_185 HEAD after the edit lands."
    • Probe: GTP-2, GTP-4, GTP-7
    • Foundations-pin: comment-id=IC_kwDOR2e1q88AAAABBUrwbg edited-at=2026-05-05T22:59:36Z
  • AC-3: The AST-extracted body of MMModule.forward on issue_185 HEAD contains zero occurrences of the substrings comfy.model_management.get_torch_device and vid = vid.to(device) — verified by committed artifacts github_issues/185/slice1/forward_source.txt (the extracted body) and github_issues/185/slice1/forbidden_call_grep.log (grep -n -E '...' forward_source.txt; echo "exit=$?" records exit=1, zero matching lines). The remaining body lines from GTP-4 (the four-line def forward(...) header through the Tuple[...] return type, the vid_module = ... line, the vid = vid_module(vid, ...) line, the if not self.vid_only: block including line 629's txt = txt.to(device=vid.device, dtype=vid.dtype) per GTP-3, and the final return vid, txt) are preserved verbatim. Note: GTP-3's .to(device=vid.device, dtype=vid.dtype) cast at line 629 is not removed by this slice; it is a derived-device cast (Out of Scope §) and is reported by Phase 0 for completeness only.
    • Pre-fix-fingerprint: §Diagnosis Summary — GTP-4 shows comfy.model_management.get_torch_device at line 624 and vid = vid.to(device) at line 625 of the pre-fix forward body.
    • Post-fix-expectation: github_issues/185/slice1/forward_source.txt shows neither substring; github_issues/185/slice1/forbidden_call_grep.log records exit code 1 with zero matches.
    • Assumes-passed: none
    • In-scope-of: Slice 1 Objective "Prove that MMModule.forward() no longer reads comfy.model_management.get_torch_device() nor forces vid = vid.to(device), that the change is scoped strictly to the body of MMModule.forward (no other method body, no other class touched), that the repository-level invariant against get_torch_device() calls inside any forward method body in comfy/ldm/seedvr/model.py is locked in by an AST-walked regression, that an nn.Identity-shaped smoke through MMModule.forward(vid, txt) succeeds without invoking get_torch_device (verified via monkeypatch that raises if invoked) and preserves input device placement, and that the existing 12-test SeedVR2 surface remains pytest-green on issue_185 HEAD after the edit lands."
    • Probe: GTP-3, GTP-4
    • Foundations-pin: comment-id=IC_kwDOR2e1q88AAAABBUrwbg edited-at=2026-05-05T22:59:36Z
  • AC-4: cd /home/johnj/dev_cuda_1/ComfyUI && git diff origin/issue_101..issue_185 -- comfy/ldm/seedvr/model.py produces hunks confined to the line range of MMModule.forward on the origin/issue_101 side (lines 613–631 per GTP-1) and zero hunks touching any of the other 16 forward methods enumerated by GTP-1 (forward 34-51, 369-399, 422-439, 511-548, 686-687, 731-828, 842-846, 864-865, 932-969, 984-993, 996-1018, 1033-1043, 1046-1064, 1095-1145, 1167-1189, 1387-1471) or any other class definition in the file — verified by committed artifact github_issues/185/slice1/scope_diff.txt. The diff shows exactly two deleted lines (text matching original lines 624 and 625 from GTP-4) and zero added lines. The artifact contains the verbatim diff plus a final stanza listing every @@-hunk header range and confirming each falls inside [613, 631] on the - side.
    • Pre-fix-fingerprint: §Diagnosis Summary — GTP-1 enumerates 17 forward methods; GTP-2 localizes the only get_torch_device() call site to MMModule.forward line 624. Any diff hunk outside [613, 631] is extraneous to the diagnosed defect.
    • Post-fix-expectation: github_issues/185/slice1/scope_diff.txt enumerates only in-range hunks with two deletions and zero additions.
    • Assumes-passed: none
    • In-scope-of: Slice 1 Objective "Prove that MMModule.forward() no longer reads comfy.model_management.get_torch_device() nor forces vid = vid.to(device), that the change is scoped strictly to the body of MMModule.forward (no other method body, no other class touched), that the repository-level invariant against get_torch_device() calls inside any forward method body in comfy/ldm/seedvr/model.py is locked in by an AST-walked regression, that an nn.Identity-shaped smoke through MMModule.forward(vid, txt) succeeds without invoking get_torch_device (verified via monkeypatch that raises if invoked) and preserves input device placement, and that the existing 12-test SeedVR2 surface remains pytest-green on issue_185 HEAD after the edit lands."
    • Probe: GTP-1, GTP-2, GTP-4
    • Foundations-pin: comment-id=IC_kwDOR2e1q88AAAABBUrwbg edited-at=2026-05-05T22:59:36Z
  • AC-5: The committed test file tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py on issue_185 defines exactly two pytest test functions whose names are test_no_get_torch_device_in_forward_methods and test_mmmodule_forward_succeeds_without_get_torch_device_lookup. The first runs tree = ast.parse(inspect.getsource(comfy.ldm.seedvr.model)) and asserts [(n.lineno, i.lineno) for n in ast.walk(tree) if isinstance(n, ast.FunctionDef) and n.name == "forward" for i in ast.walk(n) if isinstance(i, ast.Call) and isinstance(i.func, ast.Attribute) and i.func.attr == "get_torch_device"] == []. The second uses monkeypatch.setattr(comfy.model_management, "get_torch_device", boom) where boom is a zero-arg callable that increments call_count[0] and raises RuntimeError, instantiates MMModule(_IdentityCallable, shared_weights=False, vid_only=False) with a local _IdentityCallable(nn.Module) whose forward(x, *args, **kwargs) returns x, invokes mm.forward(torch.zeros(2, 4), torch.ones(2, 4)), and asserts (a) call_count[0] == 0, (b) torch.equal(vid_out, vid_in), (c) torch.equal(txt_out, txt_in), (d) vid_out.device == vid_in.device, (e) txt_out.device == txt_in.device — verified by committed artifact github_issues/185/slice1/test_source.txt (cp /home/johnj/dev_cuda_1/ComfyUI/tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py github_issues/185/slice1/test_source.txt) plus the AC-1 pytest log showing both test names in the PASSED lines.
    • Pre-fix-fingerprint: §Diagnosis Summary — GTP-5 confirms test_seedvr_forward_no_device_cast.py does not exist on origin/issue_101 HEAD.
    • Post-fix-expectation: github_issues/185/slice1/test_source.txt adds the two named tests and github_issues/185/slice1/pytest_post_fix.log shows both PASS.
    • Assumes-passed: none
    • In-scope-of: Slice 1 Objective "Prove that MMModule.forward() no longer reads comfy.model_management.get_torch_device() nor forces vid = vid.to(device), that the change is scoped strictly to the body of MMModule.forward (no other method body, no other class touched), that the repository-level invariant against get_torch_device() calls inside any forward method body in comfy/ldm/seedvr/model.py is locked in by an AST-walked regression, that an nn.Identity-shaped smoke through MMModule.forward(vid, txt) succeeds without invoking get_torch_device (verified via monkeypatch that raises if invoked) and preserves input device placement, and that the existing 12-test SeedVR2 surface remains pytest-green on issue_185 HEAD after the edit lands."
    • Probe: GTP-1, GTP-2, GTP-5, GTP-6, GTP-7
    • Foundations-pin: comment-id=IC_kwDOR2e1q88AAAABBUrwbg edited-at=2026-05-05T22:59:36Z
  • AC-6: cd /home/johnj/dev_cuda_1/ComfyUI && python3 -m pytest tests-unit/comfy_test/seedvr_model_test.py tests-unit/comfy_test/seedvr_vae_forward_test.py tests-unit/comfy_test/seedvr_vae_wrapper_forward_test.py tests-unit/comfy_test/test_seedvr_groupnorm_limit.py tests-unit/comfy_test/test_seedvr_rope_delegation.py tests-unit/comfy_test/test_seedvr_vae_decode_batch_axes.py tests-unit/comfy_test/test_seedvr_vae_decode_guards.py tests-unit/comfy_test/test_seedvr_vae_decode_unpadded_t.py tests-unit/comfy_test/test_seedvr_vae_loader_metadata.py tests-unit/comfy_test/test_seedvr_vae_tiled_args_no_mutate.py tests-unit/comfy_extras_test/test_seedvr_conditioning_hardening.py tests-unit/comfy_extras_test/test_seedvr_node_signature.py -v exits 0 on issue_185 HEAD with the final summary line containing passed and zero failed / zero errors — verified by committed artifact github_issues/185/slice1/companion_pytest.log. The 12 file paths in the command match GTP-5 verbatim.
    • Pre-fix-fingerprint: §Diagnosis Summary — github_issues/185/slice1/pytest_pre_fix.log records the dedicated failing behavior under audit on the pre-fix tree: assert not found with [(613, 624)] plus RuntimeError: MMModule.forward called get_torch_device(), while the 12 legacy SeedVR2 tests listed in GTP-5 are already green on origin/issue_101.
    • Post-fix-expectation: github_issues/185/slice1/pytest_post_fix.log flips the dedicated new test to 2 passed, and github_issues/185/slice1/companion_pytest.log shows the 12 legacy SeedVR2 tests remain green with zero failed / zero errors.
    • Assumes-passed: none
    • In-scope-of: Slice 1 Objective "Prove that MMModule.forward() no longer reads comfy.model_management.get_torch_device() nor forces vid = vid.to(device), that the change is scoped strictly to the body of MMModule.forward (no other method body, no other class touched), that the repository-level invariant against get_torch_device() calls inside any forward method body in comfy/ldm/seedvr/model.py is locked in by an AST-walked regression, that an nn.Identity-shaped smoke through MMModule.forward(vid, txt) succeeds without invoking get_torch_device (verified via monkeypatch that raises if invoked) and preserves input device placement, and that the existing 12-test SeedVR2 surface remains pytest-green on issue_185 HEAD after the edit lands."
    • Probe: GTP-5
    • Foundations-pin: comment-id=IC_kwDOR2e1q88AAAABBUrwbg edited-at=2026-05-05T22:59:36Z

Constraints

  • Python: python3
  • Runner: python3 -m pytest <test-path> -v invoked from the deliverable checkout root /home/johnj/dev_cuda_1/ComfyUI
  • No packages installed into host venv; the deliverable slot's .venv/ already provides torch 2.11.0+cu130 and pytest per GTP-8 and the slot layout in CLAUDE.md
  • No pkill, no rm -rf, no python main.py, no /tmp writes
  • One command per shell call; no &&/||/; chaining
  • All deliverable code commits land on pollockjj/ComfyUI:issue_185 cut from origin/issue_101 (HEAD 98df2309); PR target = issue_101
  • All bookkeeping evidence commits (github_issues/185/slice1/*) land on pollockjj/mydevelopment:issue_185 cut from main; PR target = main (standard [bookkeeping] [Issue 101][eval-and-fix] pattern)
  • Zero modifications to any method other than MMModule.forward in comfy/ldm/seedvr/model.py; the other 16 forward methods enumerated by GTP-1 are off-limits, as are MMModule.__init__, every other class in the file (CustomRMSNorm, MMArg, RotaryEmbeddingBase, MMRotaryEmbeddingBase, NaMMRotaryEmbedding3d, NaMMAttention, AdaLayerNorm, TimeEmbedding, NaDiT, etc.), and every module-level function (safe_pad_operation, get_args, get_kwargs, get_window_op, etc.)
  • Zero modifications to comfy/ldm/seedvr/vae.py, comfy_extras/nodes_seedvr.py, comfy/sd.py, or any other file in the deliverable repo
  • Zero changes to MMModule.forward's signature or return-type annotation (the def forward(self, vid, txt, *args, **kwargs) -> Tuple[torch.FloatTensor, torch.FloatTensor]: shape from GTP-4 lines 613–622 is preserved verbatim)
  • Zero attempts to push back to upstream Comfy-Org/ComfyUI#11294 or yousef-rafat:seedvr2
  • Relevant Existing Tooling:
    • pytest from the deliverable slot's .venv/ — REUSE; no install
    • comfy.ldm.seedvr.model.MMModule / MMArg / get_args / get_kwargs import paths — REUSE; verified by GTP-6
    • comfy.model_management.get_torch_device — REUSE; verified by GTP-7
    • comfy.cli_args.args.cpu = True import-time guard pattern — REUSE; established in tests-unit/comfy_test/test_seedvr_groupnorm_limit.py and tests-unit/comfy_test/seedvr_model_test.py
    • git, grep, AST extraction via stdlib ast module, monkeypatch fixture — REUSE
    • The new test file is the only added Python source in the deliverable; the new artifacts under github_issues/185/slice1/ are the only added paths in the bookkeeping repo
  • Evidence Primitive Requirements:
    • Behavior change (MMModule.forward stops calling get_torch_device()) → A/B pytest pair: post-fix log on issue_185 HEAD plus pre-fix log captured with model.py transiently reverted to origin/issue_101 (Slice 1 AC-1, AC-2)
    • Source shape claim (no comfy.model_management.get_torch_device and no vid = vid.to(device) in MMModule.forward body) → AST-extracted committed source plus zero-match grep transcript (Slice 1 AC-3)
    • Scope claim (only MMModule.forward changed) → committed git diff origin/issue_101..issue_185 -- comfy/ldm/seedvr/model.py with hunk-range enumeration (Slice 1 AC-4)
    • Test surface claim (two named functions with AST + monkeypatch + boom-counter assertions) → committed copy of the test source plus pytest log naming each function as PASSED (Slice 1 AC-5)
    • Companion regression claim (12 existing SeedVR2 tests remain green) → committed pytest log showing all 12 file paths exit 0 (Slice 1 AC-6)
  • Repository Hygiene:
    • Every dirty repo state on either deliverable or bookkeeping checkout is resolved by the agent without git stash or external decision deferral.
    • Dirty paths are classified as committed work, precise committed .gitignore rules for safe generated bulk, or discarded generated debris.
    • Clean committed work already present on issue_185 (e.g. the ?? github_issues/185/ dispatch artifacts seen at plan time on the bookkeeping checkout) is repo provenance and is integrated, not removed as dirty-state cleanup.
    • Slicers and Melian must run live git status --short gates locally on both /home/johnj/dev_cuda_1/ComfyUI and /home/johnj/dev_cuda_1/mydevelopment and must refuse to submit while either is dirty.
    • The transient revert in Slice 1 AC-2 (git checkout origin/issue_101 -- comfy/ldm/seedvr/model.py) MUST be undone via git restore --source=HEAD comfy/ldm/seedvr/model.py before any commit is created on the deliverable; the pre-fix log itself is committed only to the bookkeeping repo.

Out of Scope

  • comfy/ldm/seedvr/model.py:MMModule.forward:629 txt = txt.to(device=vid.device, dtype=vid.dtype) — Phase 0 GTP-3 reports this as the only .to(device=...) keyword-form cast inside any forward method body in the file. Its device argument is vid.device (input-stream-derived), not comfy.model_management.get_torch_device(); it is structurally distinct from Kosinkadink's flagged pattern (get_torch_device() lookup → .to(device) cast on the looked-up value). The issue body explicitly scopes the deliverable to "the remaining cast at MMModule.forward (lines 624-625)" — GTP-2's single match — not to every .to(device=...) call site. Line 629 also carries a dtype=vid.dtype component whose load-bearing role for cross-stream dtype alignment is a separate audit question that this plan does not adjudicate. Out of scope for this issue; if a follow-up audit determines line 629 is also a violation, file a separate issue.
  • The other 16 forward methods enumerated by GTP-1 (forward 34-51, 369-399, 422-439, 511-548, 686-687, 731-828, 842-846, 864-865, 932-969, 984-993, 996-1018, 1033-1043, 1046-1064, 1095-1145, 1167-1189, 1387-1471) — Phase 0 GTP-2 confirms zero get_torch_device() calls in any of them. They are off-limits to this slice.
  • comfy/ldm/seedvr/vae.py, comfy_extras/nodes_seedvr.py, comfy/sd.py — explicitly out of scope per the issue body's Non-Goals.
  • Sister issue pollockjj/mydevelopment#110 — touches comfy/ldm/seedvr/model.py but at different sites (structure-resolution helper, RoPE cache); coordinate at merge if both land same day. The two issues edit non-overlapping regions of the same file: select image from list node / or modify SaveImage Comfy-Org/ComfyUI#185 edits MMModule.forward (lines 613–631), Add SeedVR2 support #110 edits the structure-resolution helper and the RoPE cache (different methods per the issue body cross-reference). Neither blocks the other on different methods; the slicer's git diff (AC-4) confines hunks to [613, 631] precisely so the merge can be linear.
  • Upstream PR feat: Add support For SeedVR2 (CORE-6) Comfy-Org/ComfyUI#11294mergeable=DIRTY and silent for two weeks per the issue body; pushing fixes back to yousef-rafat:seedvr2 from this issue is out of scope and requires a separate decision.
  • Broad refactor of SeedVR2's model-management integration (Kosinkadink's "passed in ops, load_device" comment is context, not mandate). The slice removes two lines and adds one test file; it does not propagate load_device through MMModule's constructor or rewire weight placement.
  • Numeric correctness of any forward-pass output. The smoke test exercises MMModule.forward against an nn.Identity-shaped stub (_IdentityCallable.forward(x) returns x); correctness of real SeedVR2 transformer math is covered by the existing seedvr workflow integration on issue_101 and is not re-validated by this slice.
  • Tolerance-band assertions of any kind. The fix is a contract repair; equality is binary (call_count[0] == 0, torch.equal(...), vid_out.device == vid_in.device, AST [] == []).
  • Removal or refactor of any other line of MMModule.forward beyond removing the two cast lines (624–625). The four-line def forward(...) header through the Tuple[...] return type, vid_module = ..., vid = vid_module(vid, ...), the if not self.vid_only: block (including line 629's derived-device cast), and the return vid, txt are preserved verbatim.

Tracked in pollockjj/mydevelopment Comfy-Org#185 (bookkeeping PR pollockjj/mydevelopment#214).

pollockjj added 3 commits May 5, 2026 18:18
…closes pollockjj/mydevelopment#185)

Kosinkadink's PR Comfy-Org#11294 review (comment 4259303182) flagged five
forward-path device-override sites in comfy/ldm/seedvr/model.py.
Yousef removed four; MMModule.forward retained the residual:

    device = comfy.model_management.get_torch_device()
    vid = vid.to(device)

This pattern fights Comfy's model-management system: load_device
already places parameters on a chosen device, and inputs should
arrive on the same device. The global get_torch_device() call
overrides whatever placement Comfy decided.

The cast is removed; comfy.model_management is no longer referenced
in this file (import dropped). The surviving line below the removed
cast (txt = txt.to(device=vid.device, dtype=vid.dtype)) follows
vid.device — Comfy-native pattern, identical to the one
NaMMRotaryEmbedding3d.forward already uses.

Regression coverage in tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py
pins both layers of the fix:

  1. Source-level (AST-walked): no forward method body in
     comfy/ldm/seedvr/model.py calls get_torch_device(); the file
     no longer references comfy.model_management at all.
  2. Smoke forward: MMModule(nn.Linear, ...) runs on a CUDA device
     and outputs stay on the input device — under shared_weights,
     vid_only, and standard branches.

74 pre-existing SeedVR2 tests remain bit-identical pass.
…lign tests to contract

Restore the top-level ``import comfy.model_management`` so the diff
between origin/issue_101 and issue_185 contains exactly the two cast
lines inside ``MMModule.forward`` (lines 624-625 on origin/issue_101)
and nothing else. Rewrite ``test_seedvr_forward_no_device_cast.py`` to
two pytest functions matching the slice contract:

  - test_no_get_torch_device_in_forward_methods: AST-walks every
    ``forward`` method body in comfy/ldm/seedvr/model.py and asserts
    none calls ``get_torch_device``. Reports ``(func_lineno,
    call_lineno)`` so the pre-fix failure produces ``[(613, 624)]``.

  - test_mmmodule_forward_succeeds_without_get_torch_device_lookup:
    monkeypatches ``comfy.model_management.get_torch_device`` to raise,
    then runs ``MMModule(nn.Linear, 16, 8, shared_weights=False)``
    forward. With the cast removed the call must complete; with the
    cast present it raises the planted RuntimeError.

Closes pollockjj/mydevelopment#185
… slice 1 contract

Rewrite the second test to match the contract shape: zero-arg boom that
increments call_count[0] before raising, local _IdentityCallable nn.Module
returning x, MMModule(_IdentityCallable, shared_weights=False, vid_only=False),
mm.forward(torch.zeros(2, 4), torch.ones(2, 4)), and assert call_count[0] == 0
plus identity/device equality on both outputs. Rewrite the first test to use
the contract list-comprehension shape against ast.parse(inspect.getsource(...))
on the seedvr model module.
Copilot AI review requested due to automatic review settings May 5, 2026 23:58
@qa-agent-seveneves
Copy link
Copy Markdown

Codex Review — Cycle 1

Overall: patch is correct
Overall confidence: 0.93
Explanation: I found no blocking correctness issues in the PR diff. The targeted new regression test and the companion SeedVR test set pass under the repository virtualenv.

No findings.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Removes the last SeedVR2 MMModule.forward device lookup/cast that bypassed ComfyUI's model-management placement, and adds a regression test module to lock that invariant in place.

Changes:

  • Deletes the comfy.model_management.get_torch_device() lookup and vid.to(device) cast from MMModule.forward.
  • Adds an AST-based regression test asserting no forward method in comfy.ldm.seedvr.model calls get_torch_device.
  • Adds a smoke test proving MMModule.forward completes without touching get_torch_device.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
comfy/ldm/seedvr/model.py Removes the residual forward-path global device cast from MMModule.forward.
tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py Adds regression coverage for the no-lookup invariant and a direct MMModule.forward smoke test.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@tdd-agent
Copy link
Copy Markdown

tdd-agent Bot commented May 6, 2026

Melian PR-Review — Cycle 1 — DECISION: STOP — PR-review cycle converged.

HEAD SHA reviewed: 182257924715
Verdict: PASS

Why we stopped

Per the PR-review gate rule (2026-05-05), the cycle stops iff both hold:

  1. Zero codex P0/P1/P2 findings, AND
  2. Zero findings of ANY severity on load-bearing (production) code paths.

Both conditions are met. Any remaining advisory findings below are on test/doc paths or have been ACCEPT-classified by the slicer — they are converged work, not blockers.

Raw signal

  • codex: overall_correctness='patch is correct', confidence=0.93, findings=0
  • copilot: state='COMMENTED', inline_comments=0

Verdict rationale (raw)

both reviewers satisfied

This comment is posted by Melian's FSM (_handle_pr_review_triage) on every triage that results in a STOP or CONTINUE decision so the audit trail is complete.

@pollockjj pollockjj merged commit e2011b7 into issue_101 May 6, 2026
6 checks passed
@pollockjj pollockjj deleted the issue_185 branch May 19, 2026 16:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants