Skip to content

Issue #120: Verify SeedVR2 RoPE implementation reuse#21

Closed
pollockjj wants to merge 13 commits into
issue_101from
issue_120
Closed

Issue #120: Verify SeedVR2 RoPE implementation reuse#21
pollockjj wants to merge 13 commits into
issue_101from
issue_120

Conversation

@pollockjj
Copy link
Copy Markdown
Owner

@pollockjj pollockjj commented Apr 27, 2026

Issue Comfy-Org#120: Verify SeedVR2 RoPE implementation reuse

Tracking issue: https://github.com/pollockjj/mydevelopment/issues/120
Final QA PASS: https://github.com/pollockjj/mydevelopment/issues/120#issuecomment-4329927195 (qa-agent-seveneves[bot], Slice 2 PASS)

Affected Repository (this PR)

Repository Base Branch Issue Branch This PR
pollockjj/ComfyUI issue_101 issue_120 code change (test only)

Summary

Adds a parametrized regression test that locks in the wrapper-to-apply_rope1 delegation invariant of comfy.ldm.seedvr.model.apply_rotary_emb. The wrapper builds a 2x2 rotation matrix freqs_mat from freqs[..., ::2] and delegates the rotation to apply_rope1 from comfy.ldm.flux.math (live-verified at max_abs_delta = 0.0 per GTP-J/O of the plan). The test exercises the wrapper across CPU float32 / float16 / bfloat16, larger and partial-slice shapes, non-default start_index and scale, a freqs.shape[0] > t.shape[seq_dim] case to cover the wrapper's slice_at_dim path, and a CUDA-guarded float16 case.

The test asserts two invariants per case:

  1. Delegation: comfy.ldm.seedvr.model.apply_rope1 is patched with a unittest.mock.patch.object(..., wraps=...) spy. After the wrapper runs, the test asserts spy.call_count >= 1 so a future change that inlines the math and stops calling apply_rope1 fails the test.
  2. Output equivalence: torch.testing.assert_close(wrapper_out, direct_out, rtol=0, atol=0) — exact tensor equality (not bit-equality; +0.0/-0.0 and NaN payloads can still match) — against a hand-rolled reproduction of the wrapper's body. Live max_abs_delta on issue_101 HEAD is 0.0 for every case in the plan's GTP-O probe, so any future precision drift in the apply_rope1 dispatch is also caught.

Validation Summary

  • 8 parametrized cases collected: 7 CPU + 1 CUDA-guarded.
  • Local pass: <comfyui_repo>/.venv/Scripts/pytest.exe tests-unit/comfy_test/test_seedvr_rope_delegation.py -v8 passed in 1.88s on amor-fati (Windows, Python 3.13.5, pytest 9.0.3, CUDA available).
  • Mutation proof recorded in pollockjj/mydevelopment issue_120 github_issues/120/slice2/mutation_proof.md: a no-op early-return at a temporary local-only branch produced exit code 1 with assertion failures on torch.testing.assert_close(rtol=0, atol=0); the unmutated issue_120 produced exit code 0 with all cases passing.
  • No source changes to comfy/ldm/seedvr/model.py on issue_120. The mutation branch was deleted after capture.

What This PR Does Not Do

This PR does not merge and does not close issue Comfy-Org#120. It opens the test file from issue_120 against issue_101 for review, in line with the issue's parent issue #101 single-PR target. Branch and issue remain open until the parent flow handles them.

@pollockjj pollockjj marked this pull request as ready for review April 27, 2026 20:09
Copilot AI review requested due to automatic review settings April 27, 2026 20:09
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

Adds a new unit regression test to ensure comfy.ldm.seedvr.model.apply_rotary_emb continues to implement RoPE by constructing the 2x2 rotation matrix and delegating the actual rotation to comfy.ldm.flux.math.apply_rope1.

Changes:

  • Adds a parametrized pytest covering several CPU dtypes and a CUDA-guarded case.
  • Recomputes freqs_mat inline and compares the wrapper output to a direct apply_rope1 call.

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

Comment on lines +11 to +20
@pytest.fixture(scope="session", autouse=True)
def _print_apply_rotary_emb_params(pytestconfig):
names = list(inspect.signature(apply_rotary_emb).parameters)
line = f"params: {json.dumps(names)}"
tr = pytestconfig.pluginmanager.get_plugin("terminalreporter")
if tr is not None:
tr.write_line(line)
else:
print(line)
yield
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

The autouse session fixture prints the apply_rotary_emb parameter list to the test output, which adds noise to CI logs and doesn't assert anything. Consider removing this fixture, or turning it into an assertion (if you want to lock the signature), or gating the printing behind an explicit debug flag/CLI option.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

ACCEPT (already applied) — addressed in commit c51764a1 (Round 0, before this round-1 review request): the autouse session fixture and params: [...] print line were removed entirely. The current head SHA 5be4c76d (round 1 fix) does not contain any autouse=True, pytestconfig, or tr.write_line references.

Comment on lines +63 to +64
tol = _TOL[dtype]
assert torch.allclose(wrapper_out, direct_out, atol=tol)
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

The PR description calls out “byte-equivalence”, but the test currently uses torch.allclose(...) with dtype-dependent atol and the default rtol. If you really want to lock in exact delegation behavior, consider asserting exact equality (torch.equal) or using torch.testing.assert_close with rtol=0, atol=0 (or explicitly set rtol=0 if keeping atol), so small drifts don’t accidentally pass.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

ACCEPT (already applied) — addressed in commit c51764a1 (Round 0): switched from torch.allclose(..., atol=tol) with per-dtype atol literals to torch.testing.assert_close(rtol=0, atol=0). Round 1 commit 5be4c76d keeps that and rewords "byte-exact" → "exact tensor equality" per Copilot comment 3150100175.

Comment on lines +31 to +61
pytest.param("cpu", torch.float32, (1, 8, 16), id="cpu-float32-1x8x16"),
pytest.param("cpu", torch.float16, (1, 8, 16), id="cpu-float16-1x8x16"),
pytest.param("cpu", torch.bfloat16, (1, 8, 16), id="cpu-bfloat16-1x8x16"),
pytest.param("cpu", torch.float32, (2, 16, 32), id="cpu-float32-2x16x32"),
pytest.param(
"cuda",
torch.float16,
(1, 8, 16),
id="cuda-float16-1x8x16",
marks=pytest.mark.skipif(not torch.cuda.is_available(), reason="no cuda"),
),
]


@pytest.mark.parametrize("device,dtype,shape", _CASES)
def test_apply_rotary_emb_delegates_to_apply_rope1(device, dtype, shape):
torch.manual_seed(0)
t = torch.randn(*shape, dtype=dtype, device=device)
freqs = torch.randn(shape[-2], shape[-1], dtype=dtype, device=device)

wrapper_out = apply_rotary_emb(freqs, t)

rot_feats = freqs.shape[-1]
t_middle = t[..., 0:rot_feats]
angles = freqs.to(t_middle.device)[..., ::2]
cos = torch.cos(angles) * 1.0
sin = torch.sin(angles) * 1.0
col0 = torch.stack([cos, sin], dim=-1)
col1 = torch.stack([-sin, cos], dim=-1)
freqs_mat = torch.stack([col0, col1], dim=-1)
direct_out = apply_rope1(t_middle, freqs_mat)
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

This test only covers the default apply_rotary_emb call path where rot_feats == t.shape[-1] (so there is no left/right concatenation) and scale/start_index remain at defaults. If the goal is to lock in the wrapper logic around slicing/concatenation and scaling as well as apply_rope1 delegation, add at least one case where t has extra trailing features (t_right non-empty) and/or pass a non-default start_index / scale, and compare against a corresponding direct reproduction.

Suggested change
pytest.param("cpu", torch.float32, (1, 8, 16), id="cpu-float32-1x8x16"),
pytest.param("cpu", torch.float16, (1, 8, 16), id="cpu-float16-1x8x16"),
pytest.param("cpu", torch.bfloat16, (1, 8, 16), id="cpu-bfloat16-1x8x16"),
pytest.param("cpu", torch.float32, (2, 16, 32), id="cpu-float32-2x16x32"),
pytest.param(
"cuda",
torch.float16,
(1, 8, 16),
id="cuda-float16-1x8x16",
marks=pytest.mark.skipif(not torch.cuda.is_available(), reason="no cuda"),
),
]
@pytest.mark.parametrize("device,dtype,shape", _CASES)
def test_apply_rotary_emb_delegates_to_apply_rope1(device, dtype, shape):
torch.manual_seed(0)
t = torch.randn(*shape, dtype=dtype, device=device)
freqs = torch.randn(shape[-2], shape[-1], dtype=dtype, device=device)
wrapper_out = apply_rotary_emb(freqs, t)
rot_feats = freqs.shape[-1]
t_middle = t[..., 0:rot_feats]
angles = freqs.to(t_middle.device)[..., ::2]
cos = torch.cos(angles) * 1.0
sin = torch.sin(angles) * 1.0
col0 = torch.stack([cos, sin], dim=-1)
col1 = torch.stack([-sin, cos], dim=-1)
freqs_mat = torch.stack([col0, col1], dim=-1)
direct_out = apply_rope1(t_middle, freqs_mat)
pytest.param(
"cpu",
torch.float32,
(1, 8, 16),
16,
0,
1.0,
id="cpu-float32-1x8x16-default",
),
pytest.param(
"cpu",
torch.float16,
(1, 8, 16),
16,
0,
1.0,
id="cpu-float16-1x8x16-default",
),
pytest.param(
"cpu",
torch.bfloat16,
(1, 8, 16),
16,
0,
1.0,
id="cpu-bfloat16-1x8x16-default",
),
pytest.param(
"cpu",
torch.float32,
(2, 16, 32),
16,
4,
0.5,
id="cpu-float32-2x16x32-partial-start4-scale0p5",
),
pytest.param(
"cuda",
torch.float16,
(1, 8, 16),
16,
0,
1.0,
id="cuda-float16-1x8x16-default",
marks=pytest.mark.skipif(not torch.cuda.is_available(), reason="no cuda"),
),
]
@pytest.mark.parametrize(
"device,dtype,shape,rotary_dim,start_index,scale",
_CASES,
)
def test_apply_rotary_emb_delegates_to_apply_rope1(
device, dtype, shape, rotary_dim, start_index, scale
):
torch.manual_seed(0)
t = torch.randn(*shape, dtype=dtype, device=device)
freqs = torch.randn(shape[-2], rotary_dim, dtype=dtype, device=device)
wrapper_out = apply_rotary_emb(freqs, t, start_index=start_index, scale=scale)
rot_feats = freqs.shape[-1]
end_index = start_index + rot_feats
t_left = t[..., :start_index]
t_middle = t[..., start_index:end_index]
t_right = t[..., end_index:]
angles = freqs.to(t_middle.device)[..., ::2]
cos = torch.cos(angles) * scale
sin = torch.sin(angles) * scale
col0 = torch.stack([cos, sin], dim=-1)
col1 = torch.stack([-sin, cos], dim=-1)
freqs_mat = torch.stack([col0, col1], dim=-1)
direct_middle = apply_rope1(t_middle, freqs_mat)
direct_out = torch.cat((t_left, direct_middle, t_right), dim=-1)

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

MODIFY (already applied) — addressed in commit c51764a1 (Round 0): non-default start_index=4 and scale=0.5 cases were added (cpu-float32-non-empty-left-and-right-slices and cpu-float32-non-default-scale). Round 1 commit 5be4c76d adds a further freqs-longer-than-seq case per Copilot comment 3150100225. The parametrize signature kept freqs_shape as a tuple rather than the suggested rotary_dim int — equivalent coverage with explicit shape control.

Comment on lines +47 to +49
torch.manual_seed(0)
t = torch.randn(*shape, dtype=dtype, device=device)
freqs = torch.randn(shape[-2], shape[-1], dtype=dtype, device=device)
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

torch.manual_seed(0) mutates the global RNG state for the entire test process, which can make unrelated tests order-dependent if they also use torch randomness. Prefer using a local torch.Generator (passed to torch.randn) or wrapping in torch.random.fork_rng() so this test is deterministic without changing global state.

Suggested change
torch.manual_seed(0)
t = torch.randn(*shape, dtype=dtype, device=device)
freqs = torch.randn(shape[-2], shape[-1], dtype=dtype, device=device)
generator = torch.Generator(device=device).manual_seed(0)
t = torch.randn(*shape, dtype=dtype, device=device, generator=generator)
freqs = torch.randn(shape[-2], shape[-1], dtype=dtype, device=device, generator=generator)

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

ACCEPT (already applied) — addressed in commit c51764a1 (Round 0): switched from torch.manual_seed(0) to a local torch.Generator(device=device).manual_seed(0) passed to torch.randn via the generator= kwarg. No global RNG mutation.

…rtol=0/atol=0, local Generator, non-default start_index/scale coverage, drop autouse params print
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

Copilot reviewed 1 out of 1 changed files in this pull request and generated 6 comments.


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

Comment on lines +89 to +98
torch.testing.assert_close(
wrapper_out,
direct_out,
rtol=0,
atol=0,
msg=lambda m: (
f"apply_rotary_emb does not byte-match direct apply_rope1 reproduction "
f"(device={device}, dtype={dtype}, t_shape={t_shape}, "
f"freqs_shape={freqs_shape}, start_index={start_index}, scale={scale}): {m}"
),
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

The test and messages repeatedly say “byte-exact/byte-match”, but torch.testing.assert_close(rtol=0, atol=0) checks numerical equality and can still treat some bitwise differences as equal (e.g., +0.0 vs -0.0, and NaN payload/sign differences). If true byte-level stability is required, compare bit patterns explicitly; otherwise, consider rewording the docstring/message to “exact equality” or “zero-tolerance closeness” to avoid overstating what’s asserted.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

MODIFY — applied in commit 5be4c76d. Reworded "byte-exact"/"byte-match" to "exact tensor equality" in the module docstring, the assertion message, and the _direct_reproduction docstring. assert_close(rtol=0, atol=0) is exact numerical equality (+0.0 vs -0.0 and NaN payloads can still match), not bit-equality — agreed; the wording now reflects that.

Comment on lines +89 to +94
torch.testing.assert_close(
wrapper_out,
direct_out,
rtol=0,
atol=0,
msg=lambda m: (
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

PR description mentions per-dtype atol literals (e.g., 1e-6/1e-3/1e-2), but the test currently uses rtol=0, atol=0 for all dtypes. Either update the description to match the implementation or adjust the test to the intended per-dtype tolerances (keeping in mind the stated goal of catching drift).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

ACCEPT — applied. PR #21 description has been updated (#21) to drop the stale per-dtype atol mention and now describes the live test: torch.testing.assert_close(rtol=0, atol=0) plus the wraps-spy delegation check, with 8 parametrized cases.

Comment on lines +1 to +5
"""Regression test: comfy.ldm.seedvr.model.apply_rotary_emb must delegate to
comfy.ldm.flux.math.apply_rope1 with byte-exact equality across the wrapper's
slicing, scaling, and concatenation logic. Drift between the wrapper and the
delegate would silently corrupt SeedVR2's RoPE; this test fails loudly on any
future drift.
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

The test/docstring claims to enforce that apply_rotary_emb delegates to apply_rope1, but the assertion only checks output equivalence. If apply_rotary_emb were rewritten to compute the same result without calling apply_rope1, this test would still pass. To actually lock in the delegation invariant, consider monkeypatching/spying comfy.ldm.seedvr.model.apply_rope1 (the symbol used by the wrapper) and asserting it is invoked (and/or invoked with the expected freqs_mat shape) during the call.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

ACCEPT — applied in commit 5be4c76d. The test now wraps each apply_rotary_emb call inside unittest.mock.patch.object(seedvr_model, "apply_rope1", wraps=seedvr_model.apply_rope1) as spy: and asserts spy.call_count >= 1 per case. A future change that inlines the math and stops calling the imported apply_rope1 will leave call_count == 0 and fail the test. Same fix addresses codex P2 finding.

Comment on lines +36 to +45
"""Byte-for-byte reproduction of comfy/ldm/seedvr/model.py:471-505
apply_rotary_emb body, calling apply_rope1 directly on the middle slice.
"""
rot_feats = freqs.shape[-1]
end_index = start_index + rot_feats
t_left = t[..., :start_index]
t_middle = t[..., start_index:end_index]
t_right = t[..., end_index:]
angles = freqs.to(t_middle.device)[..., ::2]
cos = torch.cos(angles) * scale
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

_direct_reproduction is documented as a byte-for-byte reproduction of apply_rotary_emb (model.py:471-505), but it omits the earlier freqs_seq_dim inference and slice_at_dim(..., slice(-seq_len, None), ...) logic. Either include those steps in the reproduction and add a case where freqs is longer than seq_len (so that slicing actually changes the input), or adjust the docstring to match what’s reproduced today.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

MODIFY — applied in commit 5be4c76d. Extended _direct_reproduction to mirror the wrapper's implicit freqs_seq_dim=0 slicing path: when freqs.ndim == 2 and t.ndim == 3 and freqs.shape[0] > t.shape[seq_dim], the reproduction now slices freqs = freqs[-seq_len:] before computing freqs_mat. Coupled with the new cpu-float32-freqs-longer-than-seq parametrize case (per Copilot 3150100225), the slice_at_dim path is now covered. Updated the _direct_reproduction docstring to describe what is and isn't reproduced.

pytest.param("cpu", torch.float32, (2, 16, 32), (16, 32), 0, 1.0,
id="cpu-float32-larger"),
pytest.param("cpu", torch.float32, (1, 8, 24), (8, 16), 4, 1.0,
id="cpu-float32-non-empty-left-and-right-slices"),
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

The current parameter set never exercises the freqs = slice_at_dim(freqs, slice(-seq_len, None), ...) behavior in apply_rotary_emb, because every CPU/CUDA case uses freqs_shape[0] == seq_len. Add at least one case where freqs has a longer sequence dimension than t.shape[seq_dim] so the wrapper’s freqs-slicing path is actually covered by this regression test.

Suggested change
id="cpu-float32-non-empty-left-and-right-slices"),
id="cpu-float32-non-empty-left-and-right-slices"),
pytest.param("cpu", torch.float32, (1, 8, 16), (12, 16), 0, 1.0,
id="cpu-float32-freqs-longer-than-seq"),

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

ACCEPT — applied in commit 5be4c76d. Added pytest.param("cpu", torch.float32, (1, 8, 16), (12, 16), 0, 1.0, id="cpu-float32-freqs-longer-than-seq") so freqs.shape[0] = 12 > t.shape[seq_dim] = 8 actually exercises the wrapper's slice_at_dim(freqs, slice(-seq_len, None), dim=0) path. _direct_reproduction was extended to mirror this slicing (per Copilot 3150100217). All 8 parametrized cases pass locally.

Comment on lines +89 to +98
torch.testing.assert_close(
wrapper_out,
direct_out,
rtol=0,
atol=0,
msg=lambda m: (
f"apply_rotary_emb does not byte-match direct apply_rope1 reproduction "
f"(device={device}, dtype={dtype}, t_shape={t_shape}, "
f"freqs_shape={freqs_shape}, start_index={start_index}, scale={scale}): {m}"
),
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

torch.testing.assert_close’s msg parameter is documented as a string; passing a lambda here risks producing an unhelpful message (e.g., the function repr) or even raising a TypeError if a failure occurs. Prefer building the formatted message string eagerly (or using pytest’s assertion introspection) so failures report cleanly.

Suggested change
torch.testing.assert_close(
wrapper_out,
direct_out,
rtol=0,
atol=0,
msg=lambda m: (
f"apply_rotary_emb does not byte-match direct apply_rope1 reproduction "
f"(device={device}, dtype={dtype}, t_shape={t_shape}, "
f"freqs_shape={freqs_shape}, start_index={start_index}, scale={scale}): {m}"
),
msg = (
f"apply_rotary_emb does not byte-match direct apply_rope1 reproduction "
f"(device={device}, dtype={dtype}, t_shape={t_shape}, "
f"freqs_shape={freqs_shape}, start_index={start_index}, scale={scale})"
)
torch.testing.assert_close(
wrapper_out,
direct_out,
rtol=0,
atol=0,
msg=msg,

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

ACCEPT — applied in commit 5be4c76d. Replaced the msg=lambda m: ... callable with an eagerly-built string assigned to a local msg variable and passed to torch.testing.assert_close(..., msg=msg). Failure messages will now be plain strings, not callable reprs.

@qa-agent-seveneves
Copy link
Copy Markdown

Codex Review -- Round 1 -- KICKOFF

Runner: scripts/run_codex_review.py
PR: #21
Reviewer model: codex review (configured codex CLI default)
Kickoff timestamp (UTC): 2026-04-28T00:21:30Z

Prompt sent to codex review

Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.

Awaiting codex output. The result will be posted as a separate comment on this PR when codex exits.

@qa-agent-seveneves
Copy link
Copy Markdown

Codex Review -- Round 1 -- RESULT

PR: #21
Reviewer model: codex review (configured codex CLI default)
Result timestamp (UTC): 2026-04-28T00:22:16Z
Codex exit code: 0
Duration (s): 43.7
Detected verdict: UNKNOWN

Prompt sent to codex review

Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.

Verbatim codex review output

This PR was reviewed using codex review with the prompt above and produced the following output:


The added regression test does not actually prove the delegation invariant that the PR is intended to lock, so a core target of the test can regress while CI remains green.

Review comment:

  • [P2] Assert the delegated call rather than only equal output — C:\dev_master\ComfyUI\tests-unit\comfy_test\test_seedvr_rope_delegation.py:87-87
    Because this is meant to lock the apply_rope1 delegation invariant, comparing apply_rotary_emb against a local reimplementation is insufficient: if the wrapper is rewritten to inline the same math and stops calling apply_rope1, this test still passes. Add a spy/monkeypatch on the comfy.ldm.seedvr.model.apply_rope1 symbol used by the wrapper so the regression fails when delegation is removed.

Codex runner stderr (non-evidence)

OpenAI Codex v0.125.0 (research preview)
--------
workdir: C:\dev_master\mydevelopment
model: gpt-5.5
provider: openai
approval: never
sandbox: workspace-write [workdir, /tmp, $TMPDIR, C:\Users\JohnJ\.codex\memories]
reasoning effort: medium
reasoning summaries: none
session id: 019dd176-718f-7bb3-8690-1cecaf9f7a94
--------
user
Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.
mcp: codex_apps/github_fetch_pr started
mcp: codex_apps/github_fetch_pr (completed)
codex
The added regression test does not actually prove the delegation invariant that the PR is intended to lock, so a core target of the test can regress while CI remains green.

Review comment:

- [P2] Assert the delegated call rather than only equal output — C:\dev_master\ComfyUI\tests-unit\comfy_test\test_seedvr_rope_delegation.py:87-87
  Because this is meant to lock the `apply_rope1` delegation invariant, comparing `apply_rotary_emb` against a local reimplementation is insufficient: if the wrapper is rewritten to inline the same math and stops calling `apply_rope1`, this test still passes. Add a spy/monkeypatch on the `comfy.ldm.seedvr.model.apply_rope1` symbol used by the wrapper so the regression fails when delegation is removed.
2026-04-28T00:22:15.897807Z ERROR codex_core::session: failed to record rollout items: thread 019dd176-71e2-7880-af7b-e0cf2648c839 not found
2026-04-28T00:22:15.952098Z ERROR codex_core::session: failed to record rollout items: thread 019dd176-718f-7bb3-8690-1cecaf9f7a94 not found

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

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.


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

Comment on lines +54 to +67
# (device, dtype, t_shape, freqs_shape, start_index, scale)
_CASES = [
pytest.param("cpu", torch.float32, (1, 8, 16), (8, 16), 0, 1.0,
id="cpu-float32-base"),
pytest.param("cpu", torch.float16, (1, 8, 16), (8, 16), 0, 1.0,
id="cpu-float16-base"),
pytest.param("cpu", torch.bfloat16, (1, 8, 16), (8, 16), 0, 1.0,
id="cpu-bfloat16-base"),
pytest.param("cpu", torch.float32, (2, 16, 32), (16, 32), 0, 1.0,
id="cpu-float32-larger"),
pytest.param("cpu", torch.float32, (1, 8, 24), (8, 16), 4, 1.0,
id="cpu-float32-non-empty-left-and-right-slices"),
pytest.param("cpu", torch.float32, (1, 8, 16), (8, 16), 0, 0.5,
id="cpu-float32-non-default-scale"),
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

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

All current parametrized cases make freqs_shape[0] == t_shape[1], so the wrapper’s freqs = slice_at_dim(freqs, slice(-seq_len, None), ...) path is effectively a no-op. Consider adding at least one case where freqs has a longer sequence dimension than t (and update the direct reproduction accordingly) so the test will catch drift in the wrapper’s freqs slicing behavior as well as the apply_rope1 delegation.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

ACCEPT (already applied) — addressed in commit 5be4c76d (round 1 fix): added pytest.param("cpu", torch.float32, (1, 8, 16), (12, 16), 0, 1.0, id="cpu-float32-freqs-longer-than-seq") so freqs.shape[0] = 12 > t.shape[seq_dim] = 8 exercises the wrapper's slice_at_dim(freqs, slice(-seq_len, None), dim=0) path. _direct_reproduction was extended in lock-step to mirror that slicing. Same fix as Copilot comment 3150100225.

…5/3150100250 — apply_rope1 spy, freqs-longer-than-seq case, exact-equality wording, eager msg string
@qa-agent-seveneves
Copy link
Copy Markdown

Codex Review -- Round 2 -- KICKOFF

Runner: scripts/run_codex_review.py
PR: #21
Reviewer model: codex review (configured codex CLI default)
Kickoff timestamp (UTC): 2026-04-28T00:31:05Z

Prompt sent to codex review

Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.

Awaiting codex output. The result will be posted as a separate comment on this PR when codex exits.

@qa-agent-seveneves
Copy link
Copy Markdown

Codex Review -- Round 2 -- KICKOFF

Runner: scripts/run_codex_review.py
PR: #21
Reviewer model: codex review (configured codex CLI default)
Kickoff timestamp (UTC): 2026-04-28T00:33:59Z

Prompt sent to codex review

Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.

Awaiting codex output. The result will be posted as a separate comment on this PR when codex exits.

@qa-agent-seveneves
Copy link
Copy Markdown

Codex Review -- Round 2 -- RESULT

PR: #21
Reviewer model: codex review (configured codex CLI default)
Result timestamp (UTC): 2026-04-28T00:34:48Z
Codex exit code: 0
Duration (s): 47.5
Detected verdict: APPROVE

Prompt sent to codex review

Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.

Verbatim codex review output

This PR was reviewed using codex review with the prompt above and produced the following output:


APPROVE


Codex runner stderr (non-evidence)

OpenAI Codex v0.125.0 (research preview)
--------
workdir: C:\dev_master\mydevelopment
model: gpt-5.5
provider: openai
approval: never
sandbox: workspace-write [workdir, /tmp, $TMPDIR, C:\Users\JohnJ\.codex\memories]
reasoning effort: medium
reasoning summaries: none
session id: 019dd181-dac9-7b31-8953-b1e1b18af08a
--------
user
Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.
mcp: codex_apps/github_fetch_pr started
mcp: codex_apps/github_fetch_pr (completed)
exec
"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -Command "Get-Content -Path ..\\ComfyUI\\comfy\\ldm\\seedvr\\model.py | Select-String -Pattern 'def apply_rotary_emb' -Context 0,80" in C:\dev_master\mydevelopment
 succeeded in 314ms:

> �[7mdef apply_rotary_emb�[0m(�[0m
�[7m�[0m      freqs,�[0m
�[7m�[0m      t,�[0m
�[7m�[0m      start_index = 0,�[0m
�[7m�[0m      scale = 1.,�[0m
�[7m�[0m      seq_dim = -2,�[0m
�[7m�[0m      freqs_seq_dim = None�[0m
�[7m�[0m  ):�[0m
�[7m�[0m      dtype = t.dtype�[0m
�[7m�[0m      if not exists(freqs_seq_dim):�[0m
�[7m�[0m          if freqs.ndim == 2 or t.ndim == 3:�[0m
�[7m�[0m              freqs_seq_dim = 0�[0m
�[7m�[0m  �[0m
�[7m�[0m      if t.ndim == 3 or exists(freqs_seq_dim):�[0m
�[7m�[0m          seq_len = t.shape[seq_dim]�[0m
�[7m�[0m          freqs = slice_at_dim(freqs, slice(-seq_len, None), dim = freqs_seq_dim)�[0m
�[7m�[0m  �[0m
�[7m�[0m      rot_feats = freqs.shape[-1]�[0m
�[7m�[0m      end_index = start_index + rot_feats�[0m
�[7m�[0m  �[0m
�[7m�[0m      t_left = t[..., :start_index]�[0m
�[7m�[0m      t_middle = t[..., start_index:end_index]�[0m
�[7m�[0m      t_right = t[..., end_index:]�[0m
�[7m�[0m  �[0m
�[7m�[0m      angles = freqs.to(t_middle.device)[..., ::2]�[0m
�[7m�[0m      cos = torch.cos(angles) * scale�[0m
�[7m�[0m      sin = torch.sin(angles) * scale�[0m
�[7m�[0m  �[0m
�[7m�[0m      col0 = torch.stack([cos, sin], dim=-1)�[0m
�[7m�[0m      col1 = torch.stack([-sin, cos], dim=-1)�[0m
�[7m�[0m      freqs_mat = torch.stack([col0, col1], dim=-1)�[0m
�[7m�[0m  �[0m
�[7m�[0m      t_middle_out = apply_rope1(t_middle, freqs_mat)�[0m
�[7m�[0m      out = torch.cat((t_left, t_middle_out, t_right), dim=-1)�[0m
�[7m�[0m      return out.type(dtype)�[0m
�[7m�[0m  �[0m
�[7m�[0m  class NaMMRotaryEmbedding3d(MMRotaryEmbeddingBase):�[0m
�[7m�[0m      def __init__(self, dim: int):�[0m
�[7m�[0m          super().__init__(dim, rope_dim=3)�[0m
�[7m�[0m  �[0m
�[7m�[0m      def forward(�[0m
�[7m�[0m          self,�[0m
�[7m�[0m          vid_q: torch.FloatTensor,  # L h d�[0m
�[7m�[0m          vid_k: torch.FloatTensor,  # L h d�[0m
�[7m�[0m          vid_shape: torch.LongTensor,  # B 3�[0m
�[7m�[0m          txt_q: torch.FloatTensor,  # L h d�[0m
�[7m�[0m          txt_k: torch.FloatTensor,  # L h d�[0m
�[7m�[0m          txt_shape: torch.LongTensor,  # B 1�[0m
�[7m�[0m          cache: Cache,�[0m
�[7m�[0m      ) -> Tuple[�[0m
�[7m�[0m          torch.FloatTensor,�[0m
�[7m�[0m          torch.FloatTensor,�[0m
�[7m�[0m          torch.FloatTensor,�[0m
�[7m�[0m          torch.FloatTensor,�[0m
�[7m�[0m      ]:�[0m
�[7m�[0m          vid_freqs, txt_freqs = cache(�[0m
�[7m�[0m              "mmrope_freqs_3d",�[0m
�[7m�[0m              lambda: self.get_freqs(vid_shape, txt_shape),�[0m
�[7m�[0m          )�[0m
�[7m�[0m          target_device = vid_q.device�[0m
�[7m�[0m          if vid_freqs.device != target_device:�[0m
�[7m�[0m              vid_freqs = vid_freqs.to(target_device)�[0m
�[7m�[0m          if txt_freqs.device != target_device:�[0m
�[7m�[0m              txt_freqs = txt_freqs.to(target_device)�[0m
�[7m�[0m          vid_q = rearrange(vid_q, "L h d -> h L d")�[0m
�[7m�[0m          vid_k = rearrange(vid_k, "L h d -> h L d")�[0m
�[7m�[0m          vid_q = apply_rotary_emb(vid_freqs, vid_q.float()).to(vid_q.dtype)�[0m
�[7m�[0m          vid_k = apply_rotary_emb(vid_freqs, vid_k.float()).to(vid_k.dtype)�[0m
�[7m�[0m          vid_q = rearrange(vid_q, "h L d -> L h d")�[0m
�[7m�[0m          vid_k = rearrange(vid_k, "h L d -> L h d")�[0m
�[7m�[0m  �[0m
�[7m�[0m          txt_q = rearrange(txt_q, "L h d -> h L d")�[0m
�[7m�[0m          txt_k = rearrange(txt_k, "L h d -> h L d")�[0m
�[7m�[0m          txt_q = apply_rotary_emb(txt_freqs, txt_q.float()).to(txt_q.dtype)�[0m
�[7m�[0m          txt_k = apply_rotary_emb(txt_freqs, txt_k.float()).to(txt_k.dtype)�[0m
�[7m�[0m          txt_q = rearrange(txt_q, "h L d -> L h d")�[0m
�[7m�[0m          txt_k = rearrange(txt_k, "h L d -> L h d")�[0m
�[7m�[0m          return vid_q, vid_k, txt_q, txt_k�[0m
�[7m�[0m  �[0m
�[7m�[0m      @torch._dynamo.disable  # Disable compilation: .tolist() is data-dependent and causes graph breaks�[0m
�[7m�[0m      def get_freqs(�[0m


exec
"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -Command "Get-Content -Path ..\\ComfyUI\\comfy\\ldm\\flux\\math.py | Select-String -Pattern 'def apply_rope1' -Context 0,60" in C:\dev_master\mydevelopment
 succeeded in 298ms:

>     �[7mdef apply_rope1�[0m(x, freqs_cis):�[0m
�[7m�[0m          if comfy.model_management.in_training:�[0m
�[7m�[0m              return _apply_rope1(x, freqs_cis)�[0m
�[7m�[0m          else:�[0m
�[7m�[0m              return q_apply_rope1(x, freqs_cis)�[0m
�[7m�[0m  except:�[0m
�[7m�[0m      logging.warning("No comfy kitchen, using old apply_rope functions.")�[0m
�[7m�[0m      apply_rope = _apply_rope�[0m
�[7m�[0m      apply_rope1 = _apply_rope1�[0m


exec
"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -Command "Get-Content -Path ..\\ComfyUI\\comfy\\ldm\\flux\\math.py | Select-String -Pattern 'def _apply_rope1' -Context 0,80" in C:\dev_master\mydevelopment
 succeeded in 290ms:

> �[7mdef _apply_rope1�[0m(x: Tensor, freqs_cis: Tensor):�[0m
�[7m�[0m      x_ = x.to(dtype=freqs_cis.dtype).reshape(*x.shape[:-1], -1, 1, 2)�[0m
�[7m�[0m      if x_.shape[2] != 1 and freqs_cis.shape[2] != 1 and x_.shape[2] != freqs_cis.shape[2]:�[0m
�[7m�[0m          freqs_cis = freqs_cis[:, :, :x_.shape[2]]�[0m
�[7m�[0m  �[0m
�[7m�[0m      x_out = freqs_cis[..., 0] * x_[..., 0]�[0m
�[7m�[0m      x_out.addcmul_(freqs_cis[..., 1], x_[..., 1])�[0m
�[7m�[0m  �[0m
�[7m�[0m      return x_out.reshape(*x.shape).type_as(x)�[0m
�[7m�[0m  �[0m
�[7m�[0m  �[0m
�[7m�[0m  def _apply_rope(xq: Tensor, xk: Tensor, freqs_cis: Tensor):�[0m
�[7m�[0m      return apply_rope1(xq, freqs_cis), apply_rope1(xk, freqs_cis)�[0m
�[7m�[0m  �[0m
�[7m�[0m  �[0m
�[7m�[0m  try:�[0m
�[7m�[0m      import comfy.quant_ops�[0m
�[7m�[0m      q_apply_rope = comfy.quant_ops.ck.apply_rope�[0m
�[7m�[0m      q_apply_rope1 = comfy.quant_ops.ck.apply_rope1�[0m
�[7m�[0m      def apply_rope(xq, xk, freqs_cis):�[0m
�[7m�[0m          if comfy.model_management.in_training:�[0m
�[7m�[0m              return _apply_rope(xq, xk, freqs_cis)�[0m
�[7m�[0m          else:�[0m
�[7m�[0m              return apply_rope1(xq, freqs_cis), apply_rope1(xk, freqs_cis)�[0m
�[7m�[0m      def apply_rope1(x, freqs_cis):�[0m
�[7m�[0m          if comfy.model_management.in_training:�[0m
�[7m�[0m              return _apply_rope1(x, freqs_cis)�[0m
�[7m�[0m          else:�[0m
�[7m�[0m              return q_apply_rope1(x, freqs_cis)�[0m
�[7m�[0m  except:�[0m
�[7m�[0m      logging.warning("No comfy kitchen, using old apply_rope functions.")�[0m
�[7m�[0m      apply_rope = _apply_rope�[0m
�[7m�[0m      apply_rope1 = _apply_rope1�[0m


codex
APPROVE
2026-04-28T00:34:48.485919Z ERROR codex_core::session: failed to record rollout items: thread 019dd181-daec-7c61-a450-d82c09a28f66 not found
2026-04-28T00:34:48.540676Z ERROR codex_core::session: failed to record rollout items: thread 019dd181-dac9-7b31-8953-b1e1b18af08a not found

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

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


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


import pytest
import torch
import torch.testing
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

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

import torch.testing is unused in this file (the code uses torch.testing.assert_close via the torch module). Removing the unused import keeps the test module clean and avoids lint noise if/when linting is enabled for tests.

Suggested change
import torch.testing

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

ACCEPT — applied in commit ef0d3166. Removed the redundant import torch.testing line; import torch is sufficient on this torch version (torch.testing.assert_close resolves via torch's implicit attribute exposure of the testing submodule). Verified locally: import torch; torch.testing.assert_close(...) resolves cleanly without the explicit submodule import, and pytest still passes 8/8.

Comment on lines +11 to +25
math and stops calling ``apply_rope1`` fails the test (Copilot review on
PR #21 comment 3150100205; codex P2).
2. Compares the wrapper's output against a hand-rolled reproduction using
``torch.testing.assert_close(rtol=0, atol=0)`` -- exact tensor equality,
not bit-equality (``+0.0`` vs ``-0.0`` and NaN payloads can still match);
the assertion catches any future kernel-precision drift in the
``apply_rope1`` dispatch (Copilot review on PR #21 comments 3149914528 and
3150100175).

The test uses a local ``torch.Generator`` so global RNG state is not mutated
(Copilot review on PR #21 comment 3149914599). Parametrization covers
non-default ``start_index`` and ``scale`` and a case where
``freqs.shape[0] > t.shape[seq_dim]`` so the wrapper's
``slice_at_dim(freqs, slice(-seq_len, None), dim=0)`` path is exercised
(Copilot review on PR #21 comments 3149914553, 3150100217, 3150100225).
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

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

The module docstring contains references to PR numbers and specific Copilot comment IDs (e.g., “PR #21 comment …”). These references are brittle (they won’t be meaningful outside that PR context) and can become stale over time. Consider replacing them with a stable issue link (e.g., Issue Comfy-Org#120) or removing them so the docstring focuses on the test’s invariants and rationale.

Suggested change
math and stops calling ``apply_rope1`` fails the test (Copilot review on
PR #21 comment 3150100205; codex P2).
2. Compares the wrapper's output against a hand-rolled reproduction using
``torch.testing.assert_close(rtol=0, atol=0)`` -- exact tensor equality,
not bit-equality (``+0.0`` vs ``-0.0`` and NaN payloads can still match);
the assertion catches any future kernel-precision drift in the
``apply_rope1`` dispatch (Copilot review on PR #21 comments 3149914528 and
3150100175).
The test uses a local ``torch.Generator`` so global RNG state is not mutated
(Copilot review on PR #21 comment 3149914599). Parametrization covers
non-default ``start_index`` and ``scale`` and a case where
``freqs.shape[0] > t.shape[seq_dim]`` so the wrapper's
``slice_at_dim(freqs, slice(-seq_len, None), dim=0)`` path is exercised
(Copilot review on PR #21 comments 3149914553, 3150100217, 3150100225).
math and stops calling ``apply_rope1`` fails the test.
2. Compares the wrapper's output against a hand-rolled reproduction using
``torch.testing.assert_close(rtol=0, atol=0)`` -- exact tensor equality,
not bit-equality (``+0.0`` vs ``-0.0`` and NaN payloads can still match);
the assertion catches any future kernel-precision drift in the
``apply_rope1`` dispatch.
The test uses a local ``torch.Generator`` so global RNG state is not mutated.
Parametrization covers non-default ``start_index`` and ``scale`` and a case
where ``freqs.shape[0] > t.shape[seq_dim]`` so the wrapper's
``slice_at_dim(freqs, slice(-seq_len, None), dim=0)`` path is exercised.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

MODIFY — applied in commit ef0d3166. Replaced the inline per-comment-id and per-finding-number references in the module docstring with a single stable anchor: "Test design rationale and per-decision review trail are recorded on the tracking issue: https://github.com/pollockjj/mydevelopment/issues/120". The two assertion bullets now describe the invariants directly (delegation spy + exact-equality output) without referencing PR #21 comment IDs. Per-comment review history remains in commit messages and inline replies for archaeology.

…testing', replace per-comment-id docstring refs with stable issue link
@qa-agent-seveneves
Copy link
Copy Markdown

Codex Review -- Round 3 -- KICKOFF

Runner: scripts/run_codex_review.py
PR: #21
Reviewer model: codex review (configured codex CLI default)
Kickoff timestamp (UTC): 2026-04-28T00:45:45Z

Prompt sent to codex review

Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.

Awaiting codex output. The result will be posted as a separate comment on this PR when codex exits.

@qa-agent-seveneves
Copy link
Copy Markdown

Codex Review -- Round 3 -- RESULT

PR: #21
Reviewer model: codex review (configured codex CLI default)
Result timestamp (UTC): 2026-04-28T00:50:35Z
Codex exit code: 0
Duration (s): 288.4
Detected verdict: REQUEST_CHANGES (manual classification: 2 [P2] findings; codex did not emit a final APPROVE/REQUEST_CHANGES line on this run)

Note: This result comment is being posted manually because the original run_codex_review.py invocation in round 3 failed at the post step with HTTP 422 — the comment body included the codex runner stderr (~749KB), which exceeds GitHub's 65,536-character comment-body limit. The runner has been patched (commit c29b4f8) to truncate stderr to ~8K head+tail in result-body posts; full stderr is preserved verbatim alongside this artifact at github_issues/120/pr_review_outputs/round3_codex_stderr.txt.

Prompt sent to codex review

Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.

Verbatim codex review output

This PR was reviewed using codex review with the prompt above and produced the following output:


The SeedVR2 tiled preprocessing path uses the generic VAE tiler with an incompatible 5D layout, and the VAE decode path depends on state that is not initialized outside the custom preprocessing node. These are merge-blocking functional issues for supported user workflows.

Full review comments:

  • [P2] Route SeedVR tiling through the SeedVR tiler — C:\dev_master\ComfyUI\comfy_extras\nodes_seedvr.py:187-193
    When enable_tiling is true, this passes an already-batched 5D video tensor (b,t,h,w,c) into the generic VAE.encode_tiled(), whose 3D path unconditionally moves/unsqueezes as if the input were an unbatched frame stack and produces the wrong rank/layout before calling the SeedVR encoder. The non-tiled path avoids that because VAE.encode() only reshapes when pixel_samples.ndim < 5; tiled SeedVR preprocessing should use the SeedVR tiled_vae path or otherwise preserve the same 5D layout.

  • [P2] Initialize decode state for normal VAE decode paths — C:\dev_master\ComfyUI\comfy\ldm\seedvr\vae.py:2256-2256
    SeedVR2 is registered as a normal VAE, but decode() now unconditionally reads self.tiled_args and later self.original_image_video; those fields are only populated by SeedVR2InputProcessing. Decoding a SeedVR2 latent via standard VAE Encode/Decode, a loaded latent, or after the VAE object has been reloaded will fail with AttributeError here (or with original_image_video is None) instead of decoding or producing a controlled error.


Codex runner stderr

The full unabridged codex stderr (749,015 bytes) is preserved at github_issues/120/pr_review_outputs/round3_codex_stderr.txt on issue branch issue_120 and is not embedded here per GitHub's 65K body limit.

@pollockjj pollockjj requested a review from Copilot April 28, 2026 00:52
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

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


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

Comment on lines +27 to +28
torch._dynamo``), so running the test against the real modules is the
fail-loud-from-real-state approach this repo's tests follow.
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

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

The module docstring states that “running the test against the real modules is the fail-loud-from-real-state approach this repo's tests follow”, but other unit tests explicitly stub/mask heavy imports to avoid side effects (e.g. tests-unit/comfy_extras_test/image_stitch_test.py uses patch.dict(sys.modules, ...) to prevent CUDA init). Consider rewording this to describe the choice for this test/module specifically (or dropping the repo-wide generalization) to avoid misleading future readers.

Suggested change
torch._dynamo``), so running the test against the real modules is the
fail-loud-from-real-state approach this repo's tests follow.
torch._dynamo``), so this test intentionally runs against the real modules
to fail loudly if that import path or runtime state drifts.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

ACCEPT — applied in commit acfeb68f. Narrowed the docstring claim from a repo-wide assertion ("the fail-loud-from-real-state approach this repo's tests follow") to a test-local choice, and explicitly cited tests-unit/comfy_extras_test/image_stitch_test.py as a counter-example of stub usage so future readers see both patterns coexist. Did not adopt the verbatim suggestion (which trimmed the rationale paragraphs); instead kept the import-chain explanation and reworded only the overgeneralization. pytest 8/8 still passes, Ruff clean.

Comment on lines +88 to +91
pytest.param("cpu", torch.float16, (1, 8, 16), (8, 16), 0, 1.0,
id="cpu-float16-base"),
pytest.param("cpu", torch.bfloat16, (1, 8, 16), (8, 16), 0, 1.0,
id="cpu-bfloat16-base"),
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

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

The parametrization includes CPU float16 and bfloat16 cases, but the in-repo SeedVR2 call sites cast inputs to float32 before calling apply_rotary_emb (e.g. comfy/ldm/seedvr/model.py:435-436 and 537-545 use q.float()/k.float()). If apply_rotary_emb is only exercised with float32 on CPU in practice, these extra CPU dtypes add a stronger (and potentially unnecessary) compatibility constraint to the test. Consider limiting CPU cases to float32 and keeping the float16 coverage on CUDA (where half precision is expected).

Suggested change
pytest.param("cpu", torch.float16, (1, 8, 16), (8, 16), 0, 1.0,
id="cpu-float16-base"),
pytest.param("cpu", torch.bfloat16, (1, 8, 16), (8, 16), 0, 1.0,
id="cpu-bfloat16-base"),

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

REJECT — the issue Comfy-Org#120 plan body's Slice 2 AC-2 (https://github.com/pollockjj/mydevelopment/issues/120) literally requires this dtype coverage: AC-2: Parametrize coverage: the test in AC-1 is parametrized over at minimum these (device, dtype, shape) combinations: (cpu, torch.float32, (1, 8, 16)), (cpu, torch.float16, (1, 8, 16)), (cpu, torch.bfloat16, (1, 8, 16)), (cpu, torch.float32, (2, 16, 32)), and one CUDA case (cuda, torch.float16, (1, 8, 16)). The CPU float16 / bfloat16 cases were probed live (GTP-O) at max_abs_delta = 0.0 and locked into the AC by the planner before this PR was opened. Removing them would deviate from the approved plan AC and shrink the regression's coverage of any future caller that does pass non-float32 dtypes. The q.float()/k.float() casts at comfy/ldm/seedvr/model.py:435-436 and 537-545 are existing call-site choices, not contracts on apply_rotary_emb — the wrapper accepts arbitrary dtypes per its signature, and the regression intentionally locks behavior across all three. Skill rule reference: rejection rationale must reference a concrete project fact / plan AC (.claude/skills/pr-review/SKILL.md); plan Comfy-Org#120 Slice 2 AC-2 is the concrete fact.

…nvention to test-local choice; cite image_stitch_test.py as a counter-example of stub usage in this repo
@qa-agent-seveneves
Copy link
Copy Markdown

Codex Review — Round 4 — DISPUTE

The two [P2] findings posted in #21 (comment) are recorded as DISPUTED per the pr-review skill protocol. Same out-of-scope-per-PR-diff pattern as the round-3 disputes (#21 (comment)). Concrete project-fact rationale below; user override required to clear.

PR diff scope (live re-verification)

$ git -C /c/dev_master/ComfyUI diff --stat issue_101..issue_120
 .../comfy_test/test_seedvr_rope_delegation.py      | ... +++++++++++++++++++++
 1 file changed, ... insertions(+)

Still one file: tests-unit/comfy_test/test_seedvr_rope_delegation.py. No source modifications outside that file.

Per-finding dispute

Finding 1 — [P2] Fix tiled SeedVR input encoding shape — comfy_extras/nodes_seedvr.py:187-188

DISPUTE. Same finding family as the round-3 dispute (which codex worded as "Route SeedVR tiling through the SeedVR tiler" against the same lines). comfy_extras/nodes_seedvr.py is not modified by this PR. Plan Comfy-Org#120's "Out of Scope" list explicitly out-of-scopes any modification to SeedVR2 source. Codex's own prompt directs "Do not surface ... pre-existing bugs"; this is one. Track under issue #101 / Comfy-Org#11294.

Finding 2 — [P2] Align layer-count detection with the sentinel block — comfy/model_detection.py:504-509

DISPUTE. comfy/model_detection.py is not modified by this PR (different file from the test under review; not even in the SeedVR2 model directory). This is a pre-existing concern in the upstream model-detection logic, not introduced or modified by PR #21's regression test. Same skill rule applies: pre-existing-bug-out-of-scope per codex prompt; out-of-scope per plan Comfy-Org#120.

Skill rule referenced

.claude/skills/pr-review/SKILL.md:

The slice LLM does NOT have the authority to silently ignore a P0/P1/P2 finding — DISPUTE is recorded publicly. ... Disputed findings count as ADDRESSED only if the user later overrides; until then they remain blockers in the state file.

Both findings are recorded as DISPUTE in github_issues/120/pr_review_state.json under round4_decisions.codex_findings[].

Codex review pattern observation (4 rounds)

Codex review behavior on PR #21 across four rounds:

Round Head SHA Codex output In scope?
1 c51764a1 1 P2 ("assert delegated call") YES — addressed in 5be4c76d
2 5be4c76d APPROVE n/a
3 ef0d3166 2 P2 (nodes_seedvr.py, vae.py) NO — both pre-existing, both out of PR diff
4 04ab2a8d 2 P2 (nodes_seedvr.py again, model_detection.py) NO — both pre-existing, both out of PR diff

Rounds 3 and 4 codex reviews are surfacing pre-existing concerns in the broader SeedVR2 ecosystem that fall outside both this PR's diff and codex's own prompt rule against pre-existing bugs. Without a way to constrain codex to the PR diff, the FSM cannot autonomously converge on PR #21 by issuing more rounds — additional rounds will likely surface more pre-existing concerns in the SeedVR2 ecosystem rather than new test-file findings.

Recommendation to user

The PR-scoped review state is:

  • Codex on test file (the in-scope target): clean. Round 1 P2 was addressed; rounds 2-4 produced no test-file findings.
  • Copilot on test file: every comment has a recorded decision (16 ACCEPT/MODIFY applied + 1 REJECT with plan-AC rationale across 4 rounds).
  • Codex on broader SeedVR2 ecosystem: 4 P2 findings, all DISPUTED as out-of-scope-per-PR-diff. Track under issue Add SeedVR2 support #101 / feat: Add support For SeedVR2 (CORE-6) Comfy-Org/ComfyUI#11294.

If the user agrees the 4 codex P2 disputes are out-of-scope-for-this-PR, override them and the FSM transitions to READY. Otherwise the user makes the merge call directly.

The slice LLM is exiting the FSM reentrantly at this point — the loop cannot autonomously converge while codex keeps surfacing pre-existing-and-out-of-scope concerns. Re-invoking /pr-review pollockjj/ComfyUI 21 120 after a user override (or after a strategy change for codex) will resume.

@qa-agent-seveneves
Copy link
Copy Markdown

Codex Review -- Round 5 -- KICKOFF

Runner: scripts/run_codex_review.py
PR: #21
Reviewer model: codex review (configured codex CLI default)
Kickoff timestamp (UTC): 2026-04-28T01:12:31Z

Prompt sent to codex review

Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.

Awaiting codex output. The result will be posted as a separate comment on this PR when codex exits.

@qa-agent-seveneves
Copy link
Copy Markdown

Codex Review -- Round 5 -- RESULT

PR: #21
Reviewer model: codex review (configured codex CLI default)
Result timestamp (UTC): 2026-04-28T01:13:05Z
Codex exit code: 0
Duration (s): 32.7
Detected verdict: UNKNOWN

Prompt sent to codex review

Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.

Verbatim codex review output

This PR was reviewed using codex review with the prompt above and produced the following output:


No merge-blocking issues were found in the PR-scoped test-only diff.


Codex runner stderr (non-evidence)

OpenAI Codex v0.125.0 (research preview)
--------
workdir: C:\dev_master\mydevelopment
model: gpt-5.5
provider: openai
approval: never
sandbox: workspace-write [workdir, /tmp, $TMPDIR, C:\Users\JohnJ\.codex\memories]
reasoning effort: medium
reasoning summaries: none
session id: 019dd1a5-2389-79d0-9b6b-bef9254b7978
--------
user
Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.
mcp: codex_apps/github_fetch_pr started
mcp: codex_apps/github_fetch_pr (completed)
exec
"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -Command "git -C C:\\dev_master\\ComfyUI status --short --branch" in C:\dev_master\mydevelopment
2026-04-28T01:12:46.874955Z ERROR codex_core::tools::router: error=Exit code: 1
Wall time: 0.3 seconds
Output:
fatal: detected dubious ownership in repository at 'C:/dev_master/ComfyUI'
'C:/dev_master/ComfyUI' is owned by:
	AMOR-FATI/JohnJ (S-1-5-21-352732331-2760911773-2738897073-1001)
but the current user is:
	AMOR-FATI/CodexSandboxOffline (S-1-5-21-352732331-2760911773-2738897073-1008)
To add an exception for this directory, call:

	git config --global --add safe.directory C:/dev_master/ComfyUI

 exited 1 in 273ms:
fatal: detected dubious ownership in repository at 'C:/dev_master/ComfyUI'
'C:/dev_master/ComfyUI' is owned by:
	AMOR-FATI/JohnJ (S-1-5-21-352732331-2760911773-2738897073-1001)
but the current user is:
	AMOR-FATI/CodexSandboxOffline (S-1-5-21-352732331-2760911773-2738897073-1008)
To add an exception for this directory, call:

	git config --global --add safe.directory C:/dev_master/ComfyUI

exec
"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -Command "Select-String -Path C:\\dev_master\\ComfyUI\\comfy\\ldm\\seedvr\\model.py -Pattern \"def apply_rotary_emb\" -Context 0,60" in C:\dev_master\mydevelopment
 succeeded in 305ms:

> C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:471:�[7mdef apply_rotary_emb�[0m(�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:472:    freqs,�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:473:    t,�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:474:    start_index = 0,�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:475:    scale = 1.,�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:476:    seq_dim = -2,�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:477:    freqs_seq_dim = None�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:478:):�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:479:    dtype = t.dtype�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:480:    if not exists(freqs_seq_dim):�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:481:        if freqs.ndim == 2 or t.ndim == 3:�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:482:            freqs_seq_dim = 0�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:483:�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:484:    if t.ndim == 3 or exists(freqs_seq_dim):�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:485:        seq_len = t.shape[seq_dim]�[0m
�[7m �[0m�[7m �[0m�[7m�[0mC:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:486: �[0m�[7m �[0m�[7m �[0m�[7m �[0m�[7m �[0m�[7m �[0m�[7m �[0m�[7m �[0m�[7m�[0mfreqs �[0m�[7m�[0m= �[0m�[7m�[0mslice_at_dim(freqs, �[0m�[7m�[0mslice(-seq_len, �[0m�[7m�[0mNone), �[0m�[7m�[0mdim �[0m�[7m�[0m= �[0m
�[7m�[0mfreqs_seq_dim)�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:487:�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:488:    rot_feats = freq

... [stderr truncated: 11566 chars total; first 4000 and last 4000 shown; full stderr in round5_codex_stderr.txt] ...

l.py:525:    ]:�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:526:        vid_freqs, txt_freqs = cache(�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:527:            "mmrope_freqs_3d",�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:528:            lambda: self.get_freqs(vid_shape, txt_shape),�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:529:        )�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:530:        target_device = vid_q.device�[0m
�[7m�[0m  C:\dev_master\ComfyUI\comfy\ldm\seedvr\model.py:531:        if vid_freqs.device != target_device:�[0m


exec
"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -Command "Get-Content -Path C:\\dev_master\\ComfyUI\\tests-unit\\comfy_quant\\test_mixed_precision.py -TotalCount 80" in C:\dev_master\mydevelopment
 succeeded in 289ms:
import unittest
import torch
import sys
import os
import json

# Add comfy to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))

def has_gpu():
    return torch.cuda.is_available()

from comfy.cli_args import args
if not has_gpu():
    args.cpu = True

from comfy import ops
from comfy.quant_ops import QuantizedTensor
import comfy.utils


class SimpleModel(torch.nn.Module):
    def __init__(self, operations=ops.disable_weight_init):
        super().__init__()
        self.layer1 = operations.Linear(10, 20, device="cpu", dtype=torch.bfloat16)
        self.layer2 = operations.Linear(20, 30, device="cpu", dtype=torch.bfloat16)
        self.layer3 = operations.Linear(30, 40, device="cpu", dtype=torch.bfloat16)

    def forward(self, x):
        x = self.layer1(x)
        x = torch.nn.functional.relu(x)
        x = self.layer2(x)
        x = torch.nn.functional.relu(x)
        x = self.layer3(x)
        return x


class TestMixedPrecisionOps(unittest.TestCase):

    def test_all_layers_standard(self):
        """Test that model with no quantization works normally"""
        # Create model
        model = SimpleModel(operations=ops.mixed_precision_ops({}))

        # Initialize weights manually
        model.layer1.weight = torch.nn.Parameter(torch.randn(20, 10, dtype=torch.bfloat16))
        model.layer1.bias = torch.nn.Parameter(torch.randn(20, dtype=torch.bfloat16))
        model.layer2.weight = torch.nn.Parameter(torch.randn(30, 20, dtype=torch.bfloat16))
        model.layer2.bias = torch.nn.Parameter(torch.randn(30, dtype=torch.bfloat16))
        model.layer3.weight = torch.nn.Parameter(torch.randn(40, 30, dtype=torch.bfloat16))
        model.layer3.bias = torch.nn.Parameter(torch.randn(40, dtype=torch.bfloat16))

        # Initialize weight_function and bias_function
        for layer in [model.layer1, model.layer2, model.layer3]:
            layer.weight_function = []
            layer.bias_function = []

        # Forward pass
        input_tensor = torch.randn(5, 10, dtype=torch.bfloat16)
        output = model(input_tensor)

        self.assertEqual(output.shape, (5, 40))
        self.assertEqual(output.dtype, torch.bfloat16)

    def test_mixed_precision_load(self):
        """Test loading a mixed precision model from state dict"""
        # Configure mixed precision: layer1 is FP8, layer2 and layer3 are standard
        layer_quant_config = {
            "layer1": {
                "format": "float8_e4m3fn",
                "params": {}
            },
            "layer3": {
                "format": "float8_e4m3fn",
                "params": {}
            }
        }

        # Create state dict with mixed precision
        fp8_weight1 = torch.randn(20, 10, dtype=torch.float32).to(torch.float8_e4m3fn)

codex
No merge-blocking issues were found in the PR-scoped test-only diff.
2026-04-28T01:13:05.309149Z ERROR codex_core::session: failed to record rollout items: thread 019dd1a5-23b7-7f20-a9ec-2efdf474ae37 not found
2026-04-28T01:13:05.360342Z ERROR codex_core::session: failed to record rollout items: thread 019dd1a5-2389-79d0-9b6b-bef9254b7978 not found

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

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.


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

Comment on lines +87 to +92
# (device, dtype, t_shape, freqs_shape, start_index, scale)
_CASES = [
pytest.param("cpu", torch.float32, (1, 8, 16), (8, 16), 0, 1.0,
id="cpu-float32-base"),
pytest.param("cpu", torch.float16, (1, 8, 16), (8, 16), 0, 1.0,
id="cpu-float16-base"),
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

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

The parametrization includes a CPU torch.float16 case. On the repo’s unit-test CI this runs with the CPU PyTorch wheels (.github/workflows/test-unit.yml installs from the /whl/cpu index), and some PyTorch builds don’t implement torch.cos/torch.sin for float16 tensors on CPU (raising at runtime). Consider either removing the CPU-float16 case, or guarding it with a skip that checks whether torch.cos(torch.zeros(1, dtype=torch.float16)) works on the current runtime so CI remains stable across PyTorch versions/platforms.

Suggested change
# (device, dtype, t_shape, freqs_shape, start_index, scale)
_CASES = [
pytest.param("cpu", torch.float32, (1, 8, 16), (8, 16), 0, 1.0,
id="cpu-float32-base"),
pytest.param("cpu", torch.float16, (1, 8, 16), (8, 16), 0, 1.0,
id="cpu-float16-base"),
def _cpu_float16_trig_supported():
"""Return whether CPU float16 trig ops are available on this runtime."""
try:
torch.cos(torch.zeros(1, dtype=torch.float16))
except (RuntimeError, TypeError):
return False
return True
# (device, dtype, t_shape, freqs_shape, start_index, scale)
_CASES = [
pytest.param("cpu", torch.float32, (1, 8, 16), (8, 16), 0, 1.0,
id="cpu-float32-base"),
pytest.param(
"cpu", torch.float16, (1, 8, 16), (8, 16), 0, 1.0,
id="cpu-float16-base",
marks=pytest.mark.skipif(
not _cpu_float16_trig_supported(),
reason="torch.cos/torch.sin unsupported for float16 tensors on CPU",
),
),

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

ACCEPT — applied in commit f80a8727. Adopted the suggested skip-if-trig-unsupported pattern. Generalized to a helper _cpu_trig_supported(dtype) so the same guard applies to both cpu-float16-base and cpu-bfloat16-base cases (the bfloat16 case is in plan Comfy-Org#120 Slice 2 AC-2 and has the same potential CPU-wheel portability issue). Module-level probes _CPU_FP16_TRIG_OK / _CPU_BF16_TRIG_OK evaluate at collection time; cases skip cleanly with a clear reason on builds that don't implement those trig ops. Plan AC-2 dtype coverage is preserved (the cases stay in the parametrize list); only the runtime fan-out is adapted to the build's capability matrix. pytest 8/8 still passes locally on amor-fati (CUDA + working CPU fp16/bf16); Ruff clean.

…h pytest.mark.skipif(not _cpu_trig_supported(dtype)) so PyTorch CPU wheels that don't implement torch.cos/sin for those dtypes skip cleanly instead of failing CI; cases preserved per plan Comfy-Org#120 Slice 2 AC-2
@qa-agent-seveneves
Copy link
Copy Markdown

Codex Review -- Round 6 -- KICKOFF

Runner: scripts/run_codex_review.py
PR: #21
Reviewer model: codex review (configured codex CLI default)
Kickoff timestamp (UTC): 2026-04-28T01:19:06Z

Prompt sent to codex review

Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.

Awaiting codex output. The result will be posted as a separate comment on this PR when codex exits.

@qa-agent-seveneves
Copy link
Copy Markdown

Codex Review -- Round 6 -- RESULT

PR: #21
Reviewer model: codex review (configured codex CLI default)
Result timestamp (UTC): 2026-04-28T01:20:35Z
Codex exit code: 0
Duration (s): 86.6
Detected verdict: UNKNOWN

Prompt sent to codex review

Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.

Verbatim codex review output

This PR was reviewed using codex review with the prompt above and produced the following output:


No P0/P1/P2/P3 issues were found in the latest PR state.


Codex runner stderr (non-evidence)

OpenAI Codex v0.125.0 (research preview)
--------
workdir: C:\dev_master\mydevelopment
model: gpt-5.5
provider: openai
approval: never
sandbox: workspace-write [workdir, /tmp, $TMPDIR, C:\Users\JohnJ\.codex\memories]
reasoning effort: medium
reasoning summaries: none
session id: 019dd1ab-2a66-79b2-8fe2-3d5ce2acd4e7
--------
user
Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.
exec
"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -Command 'Get-Content -Raw debug/reset_gpu.py' in C:\dev_master\mydevelopment
 succeeded in 278ms:
#!python
from __future__ import annotations

import sys
from pathlib import Path

if __package__ in {None, ""}:
    sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from debug.harness_runtime import base_parser, detect_platform_name, render_launch_spec, run_render_only, run_reset_gpu


def main(argv: list[str] | None = None) -> int:
    parser = base_parser("Copy-exact port of debug/reset_gpu.sh")
    args = parser.parse_args(argv)
    spec = render_launch_spec(
        entrypoint="reset_gpu.py",
        mode="reset_gpu",
        platform_name=detect_platform_name(args.platform),
        workflows=[],
    )
    if args.render_spec:
        return run_render_only(spec)
    return run_reset_gpu(spec)


if __name__ == "__main__":
    raise SystemExit(main())


exec
"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -Command 'Get-Content -Raw debug/run_debug.py' in C:\dev_master\mydevelopment
 succeeded in 277ms:
#!python
from __future__ import annotations

import sys
from pathlib import Path

if __package__ in {None, ""}:
    sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from debug.harness_runtime import base_parser, detect_platform_name, render_launch_spec, run_debug_orchestrator, run_render_only


def main(argv: list[str] | None = None) -> int:
    parser = base_parser("Copy-exact port of debug/run_debug.sh")
    parser.add_argument("--workflow", action="append", default=[])
    parser.add_argument("--extra_arguments", default="")
    parser.add_argument("--copilot_prompt", default="")
    parser.add_argument("--forensic_shm", action="store_true")
    parser.add_argument("--legacy_stability_monitor", action="store_true")
    parser.add_argument("--sandbox_mode", action="store_true")
    args = parser.parse_args(argv)

    spec = render_launch_spec(
        entrypoint="run_debug.py",
        mode="run_debug",
        platform_name=detect_platform_name(args.platform),
        workflows=args.workflow,
        extra_arguments=args.extra_arguments,
        forensic_shm=args.forensic_shm,
        legacy_stability_monitor=args.legacy_stability_monitor,
        sandbox_mode=args.sandbox_mode,
    )
    if args.render_spec:
        return run_render_only(spec)
    return run_debug_orchestrator(spec)


if __name__ == "__main__":
    raise SystemExit(main())


exec
"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -Command 'Get-Content -Raw debug/harness_runtime.py' in C:\dev_master\mydevelopment
exec
"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -Command 'Get-Content -Raw debug/collect_run_artifacts.py' in C:\dev_master\mydevelopment
exec
"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -Command 'Get-Content -Raw debug/debug_comfy.py' in C:\dev_master\mydevelopment
 succeeded in 296ms:
from __future__ import annotations

import argparse
import json
import os
import re
from dataclasses import asdict, dataclass
from pathlib import Path, PurePosixPath, PureWindowsPath
from typing import Iterable


WINDOWS = "windows"
LINUX = "linux"


def _path_string(platform_

... [stderr truncated: 162770 chars total; first 4000 and last 4000 shown; full stderr in round6_codex_stderr.txt] ...


�[7m�[0m  comfy\ldm\seedvr\model.py:484:    if t.ndim == 3 or exists(freqs_seq_dim):�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:485:        seq_len = t.shape[seq_dim]�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:486:        freqs = slice_at_dim(freqs, slice(-seq_len, None), dim = freqs_seq_dim)�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:487:�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:488:    rot_feats = freqs.shape[-1]�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:489:    end_index = start_index + rot_feats�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:490:�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:491:    t_left = t[..., :start_index]�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:492:    t_middle = t[..., start_index:end_index]�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:493:    t_right = t[..., end_index:]�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:494:�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:495:    angles = freqs.to(t_middle.device)[..., ::2]�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:496:    cos = torch.cos(angles) * scale�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:497:    sin = torch.sin(angles) * scale�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:498:�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:499:    col0 = torch.stack([cos, sin], dim=-1)�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:500:    col1 = torch.stack([-sin, cos], dim=-1)�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:501:    freqs_mat = torch.stack([col0, col1], dim=-1)�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:502:�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:503:    t_middle_out = apply_rope1(t_middle, freqs_mat)�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:504:    out = torch.cat((t_left, t_middle_out, t_right), dim=-1)�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:505:    return out.type(dtype)�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:506:�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:507:class NaMMRotaryEmbedding3d(MMRotaryEmbeddingBase):�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:508:    def __init__(self, dim: int):�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:509:        super().__init__(dim, rope_dim=3)�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:510:�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:511:    def forward(�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:512:        self,�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:513:        vid_q: torch.FloatTensor,  # L h d�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:514:        vid_k: torch.FloatTensor,  # L h d�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:515:        vid_shape: torch.LongTensor,  # B 3�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:516:        txt_q: torch.FloatTensor,  # L h d�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:517:        txt_k: torch.FloatTensor,  # L h d�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:518:        txt_shape: torch.LongTensor,  # B 1�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:519:        cache: Cache,�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:520:    ) -> Tuple[�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:521:        torch.FloatTensor,�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:522:        torch.FloatTensor,�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:523:        torch.FloatTensor,�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:524:        torch.FloatTensor,�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:525:    ]:�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:526:        vid_freqs, txt_freqs = cache(�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:527:            "mmrope_freqs_3d",�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:528:            lambda: self.get_freqs(vid_shape, txt_shape),�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:529:        )�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:530:        target_device = vid_q.device�[0m
�[7m�[0m  comfy\ldm\seedvr\model.py:531:        if vid_freqs.device != target_device:�[0m


codex
No P0/P1/P2/P3 issues were found in the latest PR state.
2026-04-28T01:20:34.844482Z ERROR codex_core::session: failed to record rollout items: thread 019dd1ab-2ab8-7a51-bf96-7e1d8128db8d not found
2026-04-28T01:20:34.894566Z ERROR codex_core::session: failed to record rollout items: thread 019dd1ab-2a66-79b2-8fe2-3d5ce2acd4e7 not found

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

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.


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

the wrapper and the delegate would silently corrupt SeedVR2's RoPE; this test
fails loudly on any future drift.

Each parametrized case both:
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

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

Module docstring sentence "Each parametrized case both:" is grammatically incomplete. Consider rephrasing to "Each parametrized case does both:" (or similar) so the intro reads cleanly before the numbered list.

Suggested change
Each parametrized case both:
Each parametrized case does both:

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

ACCEPT — applied in commit 960d0ce5. Updated to "Each parametrized case does both:" per the suggestion. pytest 8/8 still passes, Ruff clean.

…se both:' -> 'each parametrized case does both:'
@qa-agent-seveneves
Copy link
Copy Markdown

Codex Review -- Round 7 -- KICKOFF

Runner: scripts/run_codex_review.py
PR: #21
Reviewer model: codex review (configured codex CLI default)
Kickoff timestamp (UTC): 2026-04-28T01:32:11Z

Prompt sent to codex review

Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.

Awaiting codex output. The result will be posted as a separate comment on this PR when codex exits.

@qa-agent-seveneves
Copy link
Copy Markdown

Codex Review -- Round 7 -- RESULT

PR: #21
Reviewer model: codex review (configured codex CLI default)
Result timestamp (UTC): 2026-04-28T01:32:49Z
Codex exit code: 0
Duration (s): 35.6
Detected verdict: UNKNOWN

Prompt sent to codex review

Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.

Verbatim codex review output

This PR was reviewed using codex review with the prompt above and produced the following output:


No P0/P1/P2/P3 issues were found in the latest PR state.


Codex runner stderr (non-evidence)

OpenAI Codex v0.125.0 (research preview)
--------
workdir: C:\dev_master\mydevelopment
model: gpt-5.5
provider: openai
approval: never
sandbox: workspace-write [workdir, /tmp, $TMPDIR, C:\Users\JohnJ\.codex\memories]
reasoning effort: medium
reasoning summaries: none
session id: 019dd1b7-24c7-7d52-a71f-3d259d550629
--------
user
Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.
mcp: codex_apps/github_fetch_pr started
mcp: codex_apps/github_fetch_pr (completed)
exec
"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -Command "Get-Content -Raw C:\\dev_master\\ComfyUI\\comfy\\ldm\\flux\\math.py" in C:\dev_master\mydevelopment
 succeeded in 287ms:
import torch
from einops import rearrange
from torch import Tensor

from comfy.ldm.modules.attention import optimized_attention
import comfy.model_management
import logging


def attention(q: Tensor, k: Tensor, v: Tensor, pe: Tensor, mask=None, transformer_options={}) -> Tensor:
    if pe is not None:
        q, k = apply_rope(q, k, pe)
    heads = q.shape[1]
    x = optimized_attention(q, k, v, heads, skip_reshape=True, mask=mask, transformer_options=transformer_options)
    return x

def rope(pos: Tensor, dim: int, theta: int) -> Tensor:
    assert dim % 2 == 0
    if not comfy.model_management.supports_fp64(pos.device):
        device = torch.device("cpu")
    else:
        device = pos.device

    scale = torch.linspace(0, (dim - 2) / dim, steps=dim//2, dtype=torch.float64, device=device)
    omega = 1.0 / (theta**scale)
    out = torch.einsum("...n,d->...nd", pos.to(dtype=torch.float32, device=device), omega)
    out = torch.stack([torch.cos(out), -torch.sin(out), torch.sin(out), torch.cos(out)], dim=-1)
    out = rearrange(out, "b n d (i j) -> b n d i j", i=2, j=2)
    return out.to(dtype=torch.float32, device=pos.device)


def _apply_rope1(x: Tensor, freqs_cis: Tensor):
    x_ = x.to(dtype=freqs_cis.dtype).reshape(*x.shape[:-1], -1, 1, 2)
    if x_.shape[2] != 1 and freqs_cis.shape[2] != 1 and x_.shape[2] != freqs_cis.shape[2]:
        freqs_cis = freqs_cis[:, :, :x_.shape[2]]

    x_out = freqs_cis[..., 0] * x_[..., 0]
    x_out.addcmul_(freqs_cis[..., 1], x_[..., 1])

    return x_out.reshape(*x.shape).type_as(x)


def _apply_rope(xq: Tensor, xk: Tensor, freqs_cis: Tensor):
    return apply_rope1(xq, freqs_cis), apply_rope1(xk, freqs_cis)


try:
    import comfy.quant_ops
    q_apply_rope = comfy.quant_ops.ck.apply_rope
    q_apply_rope1 = comfy.quant_ops.ck.apply_rope1
    def apply_rope(xq, xk, freqs_cis):
        if comfy.model_management.in_training:
            return _apply_rope(xq, xk, freqs_cis)
        else:
            return apply_rope1(xq, freqs_cis), apply_rope1(xk, freqs_cis)
    def apply_rope1(x, freqs_cis):
        if comfy.model_management.in_training:
            return _apply_rope1(x, freqs_cis)
        else:
            return q_apply_rope1(x, freqs_cis)
except:
    logging.warning("No comfy kitchen, using old apply_rope functions.")
    apply_rope = _apply_rope
    apply_rope1 = _apply_rope1


exec
"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -Command "Get-Content C:\\dev_master\\ComfyUI\\comfy\\ldm\\seedvr\\model.py | Select-Object -Index 470..506" in C:\dev_master\mydevelopment
2026-04-28T01:32:39.208846Z ERROR codex_core::tools::router: error=Exit code: 1
Wall time: 0.4 seconds
Output:
�[31;1mSelect-Object: �[0m
�[31;1m�[36;1mLine |�[0m
�[31;1m�[36;1m�[36;1m   2 | �[0m … ter\ComfyUI\comfy\ldm\seedvr\model.py | Select-Object -Index �[36;1m470..506�[0m
�[31;1m�[36;1m�[36;1m�[0m�[36;1m�[0m�[36;1m     | �[31;1m                                                                ~~~~~~~~�[0m
�[31;1m�[36;1m�[36;1m�[0m�[36;1m�[0m�[36;1m�[31;1m�[31;1m�[36;1m     | �[31;1mCannot bind parameter 'Index'. Cannot convert value "470..506" to type "System.Int32". Error: "The input string '470..506' was not in a correct format."�[0m

 exited 1 in 353ms:
�[31;1mSelect-Object: �[0m
�[31;1m�[36;1mLine |�[0m
�[31;1m�[36;1m�[36;1m   2 | �[0m … ter\ComfyUI\comfy\ldm\seedvr\model.py | Select-Object -Index �[36;1m470..506�[0m
�[31;1m�[36;1m�[36;1m�[0m�[36;1m�[0m�[36;1m     | �[31;1m                                                                ~~~~~~~~�[0m
�[31;1m�[36;1m�[36;1m�[0m�[36;1m�[0m�[36;1m�[31;1m�[31;1m�[36;1m     | �[31;1mCannot bind parameter 'Index'. Cannot convert value "470..506" to type "System.Int32". Error: "The input string '470..506' was not in a correct format."�[0m

exec
"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -Command "Get-Content C:\\dev_master\\ComfyUI\\comfy\\ldm\\seedvr\\model.py | Select-Object -Skip 470 -First 40" in C:\dev_master\mydevelopment
 succeeded in 281ms:
def apply_rotary_emb(
    freqs,
    t,
    start_index = 0,
    scale = 1.,
    seq_dim = -2,
    freqs_seq_dim = None
):
    dtype = t.dtype
    if not exists(freqs_seq_dim):
        if freqs.ndim == 2 or t.ndim == 3:
            freqs_seq_dim = 0

    if t.ndim == 3 or exists(freqs_seq_dim):
        seq_len = t.shape[seq_dim]
        freqs = slice_at_dim(freqs, slice(-seq_len, None), dim = freqs_seq_dim)

    rot_feats = freqs.shape[-1]
    end_index = start_index + rot_feats

    t_left = t[..., :start_index]
    t_middle = t[..., start_index:end_index]
    t_right = t[..., end_index:]

    angles = freqs.to(t_middle.device)[..., ::2]
    cos = torch.cos(angles) * scale
    sin = torch.sin(angles) * scale

    col0 = torch.stack([cos, sin], dim=-1)
    col1 = torch.stack([-sin, cos], dim=-1)
    freqs_mat = torch.stack([col0, col1], dim=-1)

    t_middle_out = apply_rope1(t_middle, freqs_mat)
    out = torch.cat((t_left, t_middle_out, t_right), dim=-1)
    return out.type(dtype)

class NaMMRotaryEmbedding3d(MMRotaryEmbeddingBase):
    def __init__(self, dim: int):
        super().__init__(dim, rope_dim=3)


codex
No P0/P1/P2/P3 issues were found in the latest PR state.
2026-04-28T01:32:47.657866Z ERROR codex_core::session: failed to record rollout items: thread 019dd1b7-24f6-7a80-8e96-ac5f1e259506 not found
2026-04-28T01:32:47.713997Z ERROR codex_core::session: failed to record rollout items: thread 019dd1b7-24c7-7d52-a71f-3d259d550629 not found

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

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


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

@qa-agent-seveneves
Copy link
Copy Markdown

Codex Review -- Round 1 -- KICKOFF

Runner: scripts/run_codex_review.py
PR: #21
Reviewer model: codex review (configured codex CLI default)
Kickoff timestamp (UTC): 2026-05-01T16:05:30Z

Prompt sent to codex review

Review the latest state of https://github.com/pollockjj/ComfyUI/pull/21. Output findings using strict P0-P3 priority tags ([P0], [P1], [P2], [P3]) at the start of each finding title. Surface P0/P1/P2 -- these block merge in this repo. Surface P3 with that tag explicitly. Do not surface style nits, formatting, typos, or pre-existing bugs. End your output with a single line either 'APPROVE' (no P0/P1/P2 found) or 'REQUEST_CHANGES' (one or more P0/P1/P2 found). Output nothing else after that line.

Awaiting codex output. The result will be posted as a separate comment on this PR when codex exits.

@pollockjj
Copy link
Copy Markdown
Owner Author

Superseded by #30 — consolidated into a single test PR against the clean issue_101_pi base. Old head branch (issue_120) was forked from polluted ancestor of issue_101 and presented as a 5,800-line spurious diff against the reset base. The 8 RoPE-delegation test commits from this branch are preserved in #30.

@pollockjj pollockjj closed this May 1, 2026
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

Copilot reviewed 41 out of 44 changed files in this pull request and generated 4 comments.


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

Comment on lines +1 to +35
"""Regression test: ``comfy.ldm.seedvr.model.apply_rotary_emb`` must delegate
to ``comfy.ldm.flux.math.apply_rope1`` and produce exact-equality output
across the wrapper's slicing, scaling, and concatenation logic. Drift between
the wrapper and the delegate would silently corrupt SeedVR2's RoPE; this test
fails loudly on any future drift.

Each parametrized case does both:

1. Patches ``comfy.ldm.seedvr.model.apply_rope1`` with a ``wraps``-style spy
and asserts ``spy.call_count >= 1`` so a future change that inlines the
math and stops calling ``apply_rope1`` fails the test.
2. Compares the wrapper's output against a hand-rolled reproduction using
``torch.testing.assert_close(rtol=0, atol=0)`` -- exact tensor equality,
not bit-equality (``+0.0`` vs ``-0.0`` and NaN payloads can still match);
the assertion catches any future kernel-precision drift in the
``apply_rope1`` dispatch.

The test uses a local ``torch.Generator`` so global RNG state is not mutated.
Parametrization covers non-default ``start_index`` and ``scale`` and a case
where ``freqs.shape[0] > t.shape[seq_dim]`` so the wrapper's
``slice_at_dim(freqs, slice(-seq_len, None), dim=0)`` path is exercised.
Imports are taken at module level. Heavy-import stubbing of
``comfy.model_management`` was attempted but is insufficient on this live
import chain (``comfy.ldm.seedvr.model`` pulls
``comfy.ldm.modules.diffusionmodules.model -> comfy.ops ->
comfy.memory_management -> comfy.quant_ops -> comfy_kitchen.tensor ->
torch._dynamo``), so this test intentionally runs against the real modules
to fail loudly if that import path or runtime state drifts. Other tests in
this repo (e.g. ``tests-unit/comfy_extras_test/image_stitch_test.py``) do
stub via ``patch.dict(sys.modules, ...)`` for narrower targets; the choice
here is local to this regression and not a repo-wide convention.

Test design rationale and per-decision review trail are recorded on the
tracking issue: https://github.com/pollockjj/mydevelopment/issues/120
"""
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

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

PR description says this repo change is “test only”, but this PR also adds/changes substantial runtime code paths (e.g., new SeedVR2 model/VAE integration, attention backend changes, nodes loading). Please update the PR description (or split into separate PRs) so reviewers and release notes correctly reflect the scope and risk of these changes.

Copilot uses AI. Check for mistakes.
Comment thread comfy/latent_formats.py
Comment on lines 758 to 761
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

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

SeedVR2 latent format sets latent_dimensions = 16, which is inconsistent with other video latent formats in this file (e.g., HunyuanVideo15 uses latent_dimensions = 3 for (T,H,W)). If SeedVR2 latents are video-like (the VAE path sets latent_dim = 3), this value is likely incorrect and can break latent shape handling. Please confirm the intended latent dimensionality and adjust latent_dimensions accordingly (likely 3).

Copilot uses AI. Check for mistakes.
Comment thread comfy/ldm/seedvr/vae.py
Comment on lines 24 to 30
@torch.inference_mode()
def tiled_vae(x, vae_model, tile_size=(512, 512), tile_overlap=(64, 64), temporal_size=16, encode=True, **kwargs):

gc.collect()
torch.cuda.empty_cache()

x = x.to(next(vae_model.parameters()).dtype)
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

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

tiled_vae() unconditionally calls torch.cuda.empty_cache(). On CPU-only PyTorch builds (no CUDA compiled in), this can raise at runtime; it also bypasses ComfyUI’s device-aware cache clearing helpers. Prefer using comfy.model_management.soft_empty_cache() (or at least guarding with if torch.cuda.is_available():) so this works across CPU/MPS/XPU/NPU and doesn’t crash on CPU-only installs.

Copilot uses AI. Check for mistakes.
Comment on lines 16 to 22
def clear_vae_memory(vae_model):
for module in vae_model.modules():
if hasattr(module, "memory"):
module.memory = None
gc.collect()
torch.cuda.empty_cache()

Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

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

clear_vae_memory() unconditionally calls torch.cuda.empty_cache(). This can raise on CPU-only PyTorch builds and doesn’t handle non-CUDA accelerators. Prefer comfy.model_management.soft_empty_cache() (or guard with torch.cuda.is_available()) so the node works reliably across environments.

Copilot uses AI. Check for mistakes.
pollockjj added a commit that referenced this pull request May 1, 2026
SeedVR2 native PR regression tests (consolidates #21, #22, #24 onto issue_101_pi)
@pollockjj pollockjj deleted the issue_120 branch May 1, 2026 16:48
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