Skip to content

fix(wan/shared): validate DIFFSYNTH_ATTENTION_IMPLEMENTATION - #15

Closed
wayrise wants to merge 1 commit into
mainfrom
fix/validate-diffsynth-attention-env
Closed

fix(wan/shared): validate DIFFSYNTH_ATTENTION_IMPLEMENTATION#15
wayrise wants to merge 1 commit into
mainfrom
fix/validate-diffsynth-attention-env

Conversation

@wayrise

@wayrise wayrise commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Closes #12. Thanks @rakhimovv for the write-up — I reproduced all three modes before fixing, including the NameError.

Semantics

You asked whether an unrecognized value should raise or warn-and-fall-back. Raise, matching components.py, which is already the answer for the sibling variable:

unknown name known name, library missing
WAM_ATTENTION_IMPL ValueError warn, fall back
DIFFSYNTH_ATTENTION_IMPLEMENTATION (before) accepted accepted, then crashes
DIFFSYNTH_ATTENTION_IMPLEMENTATION (after) ValueError warn, fall back

Two env vars in one codebase behaving differently is its own trap, and the compatibility argument is thin: only five values were ever meaningful, so anyone spelling one correctly is unaffected. Anyone who is not is currently training on SDPA without knowing it, which is the outcome worth breaking loudly.

One consequence worth calling out: ATTENTION_IMPLEMENTATION is resolved at module scope, so a rejected name now raises at import rather than at first dispatch. That is deliberate — a typo should stop the run rather than quietly downgrade it, and the message names the valid choices. _log_attention_backends already wraps the import per subsystem, so diagnostics still degrade to Wan shared core : ERROR (...) instead of taking the process down.

The three modes, before and after

Verified on Python 3.10, torch 2.7.1+cu128, H200, with and without the official flash_attn 2.8.3 wheel. FA3 and sage are not installed, which is the ordinary case.

1. Uninstalled backend — was a crash at dispatch

$ DIFFSYNTH_ATTENTION_IMPLEMENTATION=flash_attention_3
# before
attention_forward: NameError: name 'flash_attn_interface' is not defined
# after
[WARNING] DIFFSYNTH_ATTENTION_IMPLEMENTATION='flash_attention_3' requested but not available, falling back to auto-detect
ATTENTION_IMPLEMENTATION = 'torch'   → attention_forward runs

2. Typo — was accepted silently

$ DIFFSYNTH_ATTENTION_IMPLEMENTATION=bogus
# before: ATTENTION_IMPLEMENTATION = 'bogus', ran as plain SDPA
# after
ValueError: Unknown DIFFSYNTH_ATTENTION_IMPLEMENTATION='bogus'.
  Choose from: ['flash_attention_3', 'flash_attention_2', 'sage_attention', 'xformers', 'torch']

3. Empty string — was reachable via the is not None guard

$ DIFFSYNTH_ATTENTION_IMPLEMENTATION=
# before: ATTENTION_IMPLEMENTATION = ''
# after : ATTENTION_IMPLEMENTATION = 'torch'

That also settles the cosmetic you and I went back and forth on in #11 — the deploy report now reads Wan shared core : torch for a blank override, with nothing added to the logger. Your call to fix the cause instead was the right one.

What changed

initialize_attention_priority() now strips and lowercases the override, rejects a name outside KNOWN_ATTENTION_IMPLEMENTATIONS, and checks the corresponding *_AVAILABLE flag before honouring it. Auto-detection is unchanged — same priority order, now driven by that tuple rather than an if/elif chain, and pinned by a test so this refactor can't have reshuffled it silently.

A valid, installed override still wins over auto-detection (flash_attention_2 requested and present → flash_attention_2; torch requested with flash-attn installed → torch, i.e. opting out still works).

Tests

Six cases in tests/test_attention_backend_dispatch.py, all CPU-only. Against origin/main's attention.py they fail 4/6 — the other two pin behaviour that was already correct (a valid available override is honoured; the auto-detection order).

full suite, no flash-attn   : 1979 passed, 20 skipped
full suite, with flash-attn : 1979 passed, 20 skipped
ruff check / ruff format --check / compileall : clean

🤖 Generated with Claude Code

initialize_attention_priority() lowercased the override and returned it unchecked,
unlike its sibling WAM_ATTENTION_IMPL, which validates against _BACKEND_MAP and warns
when the requested library is missing. Three consequences, all reported in #12:

- Naming an uninstalled backend crashed at dispatch. flash_attention_3() dereferences
  the module-level flash_attn_interface that the try/except import never bound, so
  DIFFSYNTH_ATTENTION_IMPLEMENTATION=flash_attention_3 on a box without FA3 raised
  NameError from inside the kernel wrapper.
- A typo was accepted silently. attention_forward's else-branch ran it as plain SDPA,
  so a misspelled backend was indistinguishable from a working one and a whole job
  could train on SDPA while looking like it had a fused kernel.
- The empty string was reachable, because the lookup guarded on `is not None` rather
  than truthiness, and behaved the same way. It is also why the deploy diagnostics
  logged a blank backend name.

Mirror the sibling: strip and lowercase, raise on a name outside
KNOWN_ATTENTION_IMPLEMENTATIONS, and fall back to auto-detection with a warning when
the named backend's library did not import. Auto-detection order is unchanged and
pinned by a test.

Rejecting an unknown name raises at import, since ATTENTION_IMPLEMENTATION is resolved
at module scope. That is deliberate: a typo should stop the run rather than quietly
downgrade it, and the message names the valid choices. _log_attention_backends already
catches the import and reports it per subsystem.

Closes #12.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@wayrise

wayrise commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Closing in favour of #14, which does the same job and got here first.

My mistake: I picked up #12 and wrote this without checking whether a PR was already open. @rakhimovv had pushed #14 forty minutes earlier — and had said in the issue he was ready to send one, so the fault is entirely on me for not looking.

For the record, since it's mildly reassuring rather than embarrassing: the two arrived at the same semantics independently. Both mirror components.py — unknown name raises, known-but-uninstalled warns and falls back — and both landed on treating a blank value as "no override" rather than an error.

Where they differ, #14 is the better change. It leaves the auto-detection if-chain alone; this branch restructured it into a table-driven loop and then had to add a test pinning the priority order to prove the refactor was inert. Less churn for the same fix. Its test file is also slightly broader — test_torch_is_always_selectable covers a case I missed.

I verified #14 on an H200 before saying any of this, and reviewed it there.

Nothing here is worth salvaging except possibly one invariant — a loop asserting every known backend degrades to torch when nothing is installed — which I've left as a non-blocking note on #14. Branch deleted.

@wayrise wayrise closed this Sep 8, 2026
@wayrise
wayrise deleted the fix/validate-diffsynth-attention-env branch September 8, 2026 17:04
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.

DIFFSYNTH_ATTENTION_IMPLEMENTATION is unvalidated: naming an uninstalled backend raises NameError at dispatch

1 participant