Commit Graph
6 Commits
Author SHA1 Message Date
Reithan 950b55df5f Add __version__, patch-time version+pred-type log, platform-agnostic issue template (#39)
## What
Groundwork folded ahead of the held v1.0.0 release (publish-neutral —
**`pyproject.toml` is untouched**, so this does not trigger the registry
auto-publish):

1. **`NRS.__version__`** — added `__version__ = "0.7.4"` (mirrors the
current `pyproject.toml` version) in `NRS/nodes_NRS.py`, re-exported
from a new `NRS/__init__.py` so `NRS.__version__` is importable.
Commented to bump both together at release.
2. **Patch-time log line** — `patch()` now emits, right after
prediction-type detection:
`logging.info(f"NRS v{__version__}: prediction type detected ->
{pred_type.name}")`
So a user's console shows which NRS version ran and which prediction
space it selected (the key diagnostic for the flow-family
reclassification).
3. **GitHub bug-report issue form** —
`.github/ISSUE_TEMPLATE/bug_report.yml` + `config.yml`. Captures the NRS
version + detected prediction type (from the new log line),
model/sampler family, skew/stretch/squash values, expected vs actual,
repro, and console log.
- **Platform-agnostic:** NRS ships for ComfyUI *and* the A1111 family,
so the form has a **Platform / UI** dropdown — `ComfyUI, AUTOMATIC1111,
Forge, reForge, Forge Neo, Stability Matrix, Other` — with a free-text
follow-up and a generic "Platform version" field (no ComfyUI-only
wording).

## Tests
`tests/test_version.py`: asserts `NRS.__version__` is a valid `X.Y.Z`
string, matches `pyproject.toml` (catches future drift), and (via
`caplog`) that `patch()` emits the exact version + pred-type log line.

## Notes
- Adding `NRS/__init__.py` turned `NRS` from an implicit namespace
package into a real package, which newly triggered ruff's `N999` on the
pre-existing mixed-case filename `nodes_NRS.py`. Since `pyproject.toml`
is off-limits here, it's suppressed with an in-file `# ruff: noqa: N999`
pragma (with an explanatory comment) — consistent with the existing
per-file `N802/N804` carve-out for that same file. Renaming the file is
out of scope (breaks imports).

## Validation
- `ruff check .` → clean.
- `pytest --cov=NRS --cov-branch` → **45 passed** (42 + 3 new).
- diff-cover branch gate (mirrors CI) → **100%** on changed code.
- `pyproject.toml` / `uv.lock` untouched; issue-form YAML validated with
`yaml.safe_load`.
2026-08-14 02:24:47 -07:00
Reithan c526ec9353 PR-3: FLOW operation space + flow-family reclassification (#37)
## What
Adds `PredictionType.FLOW` — an **identity operation space** — and
reclassifies the flow-matching family (`flux`, `chroma`, `flow`, `wan`,
`const`) off the VP ε-prediction path onto it. Flow models (H3, Flux,
Chroma, WAN) were resolving to `PredictionType.EPS`, so NRS ran the ε→v
/ v→ε σ-algebra on them using the wrong sigma convention. NRS's
projection/rejection geometry and norm ratios are invariant to a flow
model's per-sample velocity scale, so flow models are now operated
**natively with no conversion** (identity in, identity out).

## Changes (`NRS/nodes_NRS.py`)
- New enum member `PredictionType.FLOW`.
- `_convert_to_v_space` / `_finalize_from_v_space`: the `V` identity
branch is widened to `V | FLOW` (no ε↔v algebra, `x_div=None`,
cond/uncond/x_final passed through). **EPS** (SDXL/SD1.5), `X0`
(`NotImplementedError`), and the UNKNOWN fallback are untouched.
- `_RAW_TO_ENUM`: `flux`/`chroma`/`flow`/`wan`/`const` → `FLOW`
(comments updated). `eps`/`epsilon`/`v`/`v_prediction`/`x0`/`sample`
unchanged.
- `_get_pred_type` enhanced-detection fallback (the **second** detection
site): the CONST sampling-class branch and the `model.model.model_type`
flow/flux branch now return `FLOW` instead of `EPS` (log strings
updated). The `v_prediction`→V and `eps`→EPS branches are unchanged.
Changing only the dict would have silently left class-name /
model_type-detected models (WAN, H3) on the VP path.

## H3 specifically
MiniMax H3 needs **no special case**. It exposes `BaseModel.model_type`
as the `ModelType.FLOW` **Enum**; `_canon`'s Enum branch reduces it to
`"flow"`, an exact `_RAW_TO_ENUM` key → resolves to `FLOW` at the dict
direct-hit site. There is no `"h3"` raw type. A regression test pins
this exact Enum→canon→FLOW path.

## Tests (`tests/test_pred_type.py`)
- Flipped the flow-family pins (`flow`/`const`/`flux`/`chroma`/`wan`)
from EPS to **FLOW** — the PR-1 safety net doing its job.
- Fallback-site coverage: stubs whose only flow signal is a CONST-like
`model_sampling` class name, or a `model.model.model_type` string merely
*containing* `"flow"`/`"flux"` (not an exact dict key) — so the
enhanced-detection branch is genuinely exercised, not the dict.
- H3-representative test: an actual `enum.Enum` member
(`ModelType.FLOW`-shaped) resolves to FLOW via `_canon`'s Enum branch.
- FLOW identity round-trip through both conversion helpers; V-path
identity regression guard; EPS-path regression guard asserting the ε→v
algebra still runs.

- `uvx ruff check .` → All checks passed!
- `uvx --with torch pytest -q` → **42 passed** (up from 33)

## Rollout
Breaking for **all flow-family outputs** (Flux/Chroma/WAN/H3 produce
different images/video — retune skew/stretch). Baseline preserved at tag
`pre-flow`. This is the last breaking change before the **v1.0.0**
release.
2026-08-14 01:27:00 -07:00
Reithan d4ff852491 PR-2: pack-aware per-stream NRS routing + degeneracy tripwire (#36)
## What
Fixes NRS on MiniMax H3 (and any sampler that packs multiple streams).
ComfyUI hands the cfg hook a **flat packed latent** `[B,1,N]` whose
`dim=1` is a singleton, so every NRS reduction runs over a size-1 axis
and the projection geometry collapses — Skew becomes a silent no-op,
Stretch degenerates to elementwise CFG, Squash to a Stretch-reverter
(proven to float precision in Phase 0).

This PR unpacks the flat pack into its real channels-first per-stream
tensors, runs the **unchanged** NRS geometry per stream, then repacks.

## Changes (`NRS/nodes_NRS.py`)
- Guarded `import comfy.utils as _comfy_utils` (module still imports
with no ComfyUI present) + `import math`.
- Module-level pure-Python `_unpack_latents`/`_pack_latents` fallbacks
matching ComfyUI's pack contract (each stream reshaped to `(B,1,-1)` and
concatenated on the last dim; unpack slices `prod(shape[1:])` per
stream).
- Extracted `_apply_guidance(...)` — the per-stream geometry pipeline
(sigma reshape → `_convert_to_v_space` → dot/proj/stretch/skew/squash →
`_finalize_from_v_space`), **byte-for-byte identical** to the prior
inline math.
- Rewired `nrs()`: read `args["model"].latent_shapes`; when present with
`len>1`, unpack cond/uncond/input per stream (comfy-preferred,
pure-Python fallback), map `_apply_guidance`, repack; otherwise the
single-stream path is the **exact prior behavior** (regression no-op for
SDXL etc.).
- Permanent **degeneracy tripwire**: warns once per `patch()` if any
routed stream still has a singleton reduction axis — the silent-failure
class that cost the original Skew investigation days.

## Why this is safe
- Single-stream models are a verified byte-for-byte no-op (two
regression tests compare against a manually-computed `_apply_guidance`).
- Not H3 fingerprinting: any multi-stream packed sampler (LTXV AV
variants) benefits; older ComfyUI without the API falls through cleanly.
- Verified against the real 18MB H3 capture (not committed):
unpack→repack is bit-exact for cond/uncond/x_orig; streams recover as
`[1,24,72,38,22]` (video) / `[1,32,2,405]` (audio); per-stream guidance
produces large live deviations where the flat pack was near-degenerate —
Skew is alive again.

## Tests
New `tests/test_pack_split.py` (9 tests, real-torch via a module-scoped
isolation harness that restores the mock afterward): round-trip
pack/unpack, single- & multi-stream fallback regression, tripwire
fires/doesn't-fire, per-stream reduced shapes, and
non-degenerate-vs-degenerate rejection.

- `uvx ruff check .` → All checks passed!
- `uvx --with torch pytest -q` → **33 passed** (24 existing + 9 new)

Breaking for H3 outputs only. Baseline preserved at tag `pre-flow`.
2026-08-14 01:15:59 -07:00
Reithan 2ca0df18bc Sort imports in test_pred_type to satisfy ruff I001 (#35)
## What
Sorts the import block at `tests/test_pred_type.py:18` so `ruff` (rule
I001) passes clean on `main`.

```diff
-from NRS.nodes_NRS import PredictionType, _RAW_TO_ENUM, NRS
+from NRS.nodes_NRS import _RAW_TO_ENUM, NRS, PredictionType
```

## Why
Gate-1 (pre-refactor checkpoint) requires `ruff` + `pytest` green on
`main`. This was the only outstanding lint violation.

## Verification
- `uvx ruff check .` → All checks passed!
- `uvx --with torch pytest -q` → 24 passed

Import-sort only; no behavior change.
2026-08-14 00:56:48 -07:00
Reithan ffeb7fe4f3 PR-1: cleanup — remove mangled guard, amputate dead operation-space arms, add detection tests (#34)
## Summary

Behavior-preserving cleanup ahead of the H3 flow-fix work (see
`.claude/plans/nrs_h3_fix_plan.2.md`, fact 8 / Phase 3 / Sequencing
PR-1). No output change — pure refactor plus a regression net.

### Changes to `NRS/nodes_NRS.py`
- **Removed the broken name-mangling guard.** `hasattr(self,
"__pred_type")` checked the literal name while assignment created
`_NRS__pred_type`, so the guard never fired. `patch()` now computes
`pred_type = self._get_pred_type(model)` unconditionally and passes it
as a closure local / explicit parameter into `nrs()`,
`_convert_to_v_space`, and `_finalize_from_v_space`. This preserves the
(correct) always-redetect behavior and removes a latent cross-model
aliasing bug. The `hasattr` string was **not** "repaired" — doing so
would introduce a stale-cache bug since ComfyUI reuses node instances
across queue runs.
- **Amputated dead operation-space arms.** `self.__OPERATION_SPACE` was
hardcoded to `PredictionType.V`, making both `match` blocks and the
`_convert_to_eps_space`/`_finalize_from_eps_space` helpers unreachable.
Removed them; the `nrs()` hook now calls the V-space
conversion/finalization directly. The inner V-vs-EPS conversion math
inside the V-space helpers is untouched — that is the real per-model
algebra, not dead code. FLOW is added in a later PR.

### Tests
- New `tests/test_pred_type.py`: parametrized coverage of all 11
`_RAW_TO_ENUM` entries plus `_get_pred_type` walks over stub models
(direct-attribute path and enhanced-detection fallback). These pin
**current** behavior (flow-family → EPS) as a regression net for the
FLOW reclassification PR.

## Verification
- `24/24` tests pass (21 new + 3 pre-existing smoke) from a clean
checkout.
- Greps for `self.__pred_type`, `_NRS__pred_type`,
`self.__OPERATION_SPACE` all empty.
- Diff: `NRS/nodes_NRS.py` +16/-87, `tests/test_pred_type.py` +111 —
behavior-preserving, well under 500 LoC.
2026-08-13 20:46:47 -07:00
Reithan ee6dc40393 Add git hooks and development infrastructure (#32)
## Summary

Establishes a complete development infrastructure for the project with
automated quality checks, testing, and contributor guidelines.

- Add pre-commit hooks for automated linting and quality checks
- Configure ruff for code linting and formatting (120 char line length,
Python 3.10+)
- Set up pytest with mocked torch/gradio dependencies for CI/CD
compatibility
- Create comprehensive CONTRIBUTING.md with setup instructions and
workflow guidance
- Add uv.lock for reproducible dependency resolution
- Replace trivial tests with functional interface tests

## Key Components

### Git Hooks (.pre-commit-config.yaml)
- Pre-commit: ruff linting/formatting, trailing whitespace, YAML
validation, prevents direct commits to main
- Pre-push: runs pytest test suite, prevents direct pushes to main

### Testing (tests/)
- Functional tests for ComfyUI node and WebUI script interfaces
- Mock torch, gradio, and WebUI modules via conftest.py (no heavy
dependencies in dev env)
- Tests verify API contracts without requiring full ComfyUI/WebUI
runtime

### Documentation (CONTRIBUTING.md)
- Development setup with uv package manager
- Git workflow and branch protection guidelines
- Linting, testing, and pre-commit hook usage
- Code style guidelines and commit message conventions

## Test Plan

- [x] Pre-commit hooks run on commit (linting, formatting, checks)
- [x] Pre-push hooks run pytest successfully
- [x] All tests pass in isolated venv
- [x] CONTRIBUTING.md instructions verified
- [x] No IDE diagnostics or issues
2026-05-15 16:08:45 -07:00