Skip to content

Commit 8f90919

Browse files
committed
fix(v4-dispatch): classify PathEUnavailable as selection signal, not runtime crash (RULE #1 FIRE)
_path_e_call wrapped op.gated_delta_update in a bare `except Exception` that routed ANY exception -- including the INTENTIONAL PathEUnavailable (raised when the GDN gate is amplifying g>0, an availability/selection signal) -- through _dispatch_failure_or_fallback (the always-RAISE crash path). So forcing path_e with an amplifying gate misclassified "not eligible for this input" as a kernel crash and tripped the RULE #1 guard. Fix: catch PathEUnavailable BEFORE the bare except and route it through _fallback_or_raise (mirrors the static-unavailability routing); keep the bare except for genuine kernel crashes. Same pre-catch applied to kda_paths.py _path_e_call (KDA adapter raises PathEUnavailable for ineligible shapes). NOT a RULE #1 weakening: PathEUnavailable is an explicit selection signal (like static unavailability); _fallback_or_raise still RAISES when allow_fallback=False, and genuine crashes still route through the always-raise handler. Metal: forced path_e+amplifying+allow_fallback=True -> falls back to A (no raise); +allow_fallback=False -> raises; genuine crash -> still raises. test_path_dispatch.py 25 passed.
1 parent 0fd2072 commit 8f90919

2 files changed

Lines changed: 22 additions & 0 deletions

File tree

cppmega_v4/_tilelang/kda_paths.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,17 @@ def _path_e_call(*args, allow_fallback: bool = True, **kwargs):
255255
"path_e", _path_e_status().reason, allow_fallback, *args, **kwargs,
256256
)
257257
try:
258+
from cppmega_v4.nn._external._path_e_eligibility import PathEUnavailable
258259
from cppmega_v4.nn._external.mlx_lm_kda_update import kda_update
259260
return kda_update(*args, **kwargs)
261+
except PathEUnavailable as exc:
262+
# AVAILABILITY/SELECTION signal (e.g. an ineligible shape), NOT a
263+
# runtime crash. Route it like static unavailability above so the
264+
# dispatcher falls back to Path B/A (or RAISES when allow_fallback is
265+
# False). A genuine kernel crash still hits the always-raise handler.
266+
return _fallback_or_raise(
267+
"path_e", str(exc), allow_fallback, *args, **kwargs,
268+
)
260269
except Exception as exc:
261270
return _dispatch_failure_or_fallback(
262271
"path_e", exc, allow_fallback, *args, **kwargs,

cppmega_v4/_tilelang/linear_attention_paths.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ def _path_e_status_for_inputs(*args, **kwargs) -> PathStatus:
280280

281281

282282
def _path_e_call(*args, allow_fallback: bool = True, **kwargs):
283+
from cppmega_v4.nn._external._path_e_eligibility import PathEUnavailable
284+
283285
status = _path_e_status()
284286
if not status.available:
285287
return _fallback_or_raise(
@@ -290,6 +292,17 @@ def _path_e_call(*args, allow_fallback: bool = True, **kwargs):
290292
"cppmega_v4.nn._external.mlx_lm_gated_delta_update"
291293
)
292294
return op.gated_delta_update(*args, **kwargs)
295+
except PathEUnavailable as exc:
296+
# AVAILABILITY/SELECTION signal, NOT a runtime crash. Path E raises
297+
# PathEUnavailable (mlx_lm_gated_delta_update.py) when the gate is
298+
# amplifying (g>0) or the shape is ineligible — it explicitly cannot
299+
# represent this input. That is semantically identical to the static
300+
# unavailability routed above, so route it the same way (fall back to
301+
# Path B/A, or RAISE when allow_fallback=False). RULE #1 stays intact:
302+
# a genuine kernel crash falls through to the always-raise handler.
303+
return _fallback_or_raise(
304+
"path_e", str(exc), allow_fallback, *args, **kwargs,
305+
)
293306
except Exception as exc:
294307
return _dispatch_failure_or_fallback(
295308
"path_e", exc, allow_fallback, *args, **kwargs,

0 commit comments

Comments
 (0)