Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- A stalled or dropped read no longer abandons a `pull`: the transfer
retries with backoff from the bytes already on disk, tunable by
`GMLX_PULL_RETRIES` and `GMLX_PULL_TIMEOUT`.
- Streaming decode with the lookahead prestage no longer crashes on a
model whose router scores are bfloat16 ("'bfloat16' is not a valid
PEP 3118 buffer format string").

## [0.4.13] - 2026-09-12

Expand Down
6 changes: 5 additions & 1 deletion gmlx/stream/lookahead.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,11 @@ def predict(self, x, variant: str):
``indices``-like) for ``dst_li``."""
if variant == "ratio":
x = x * self._ratio.astype(x.dtype)
return self._router_fn(x)
ids, scores = self._router_fn(x)
# The hook reads the scores back through numpy, which has no
# bfloat16 buffer format; cast inside the lazy graph so the joint
# eval covers it and a bf16 router never crashes decode.
return ids, scores.astype(mx.float32)


class LookaheadProbe:
Expand Down
23 changes: 23 additions & 0 deletions tests/stream/test_lookahead.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,29 @@ def test_install_and_probe_end_to_end(monkeypatch):
assert cells[K] == [float(K), float(K)]


def test_bf16_router_scores_reach_numpy(monkeypatch):
"""A bf16 router's scores have no numpy buffer format; the predictor
hands them over as float32 so on_call's host copy works."""
model = _streaming_model(monkeypatch)
monkeypatch.setenv("GMLX_DECODE_LOOKAHEAD_PROBE", "1")
assert install_lookahead(model, model.layers, probe=True) == 1
la0 = model.layers[0].mlp.switch_mlp._kq_lookahead
stock = la0.predictor._router_fn

def bf16_router(x):
ids, scores = stock(x)
return ids, scores.astype(mx.bfloat16)

monkeypatch.setattr(la0.predictor, "_router_fn", bf16_router)
x = mx.random.normal((1, 1, DIM))
mx.eval(model.layers[0].mlp(x))
assert not la0.predictor.dead
preds = la0.probe._pending[la0.predictor.dst_li]
assert preds and all(isinstance(v, np.ndarray) for v in preds.values())
mx.eval(model.layers[1].mlp(x))
assert la0.probe._recall


def test_dead_predictor_disables_cleanly(monkeypatch):
model = _streaming_model(monkeypatch)
install_lookahead(model, model.layers, probe=True)
Expand Down