What's been cooking — June 2026

10 merged PRs across 4 repos

June kept last month's two threads running — more CUDA correctness on the LightGBM fork, more CoreML op builders on ONNX Runtime — and added a third: a pair of companion fixes to Exa's own SDKs, where the Python and TypeScript clients were both rejecting the API's own structured responses. Ten PRs across four repos, and a suspicious number of one-line type widenings hiding real bugs.

BelixRogner/ExaBoost

The CUDA backend gave up four more bugs. ExaBoost#17 is the big one: forcedsplits_filename was being silently ignored under device_type="cuda", because ForceSplits only ever lived in SerialTreeLearner::Train and the CUDA learner overrides Train without ever consulting the forced-split JSON — so the CPU and CUDA trees rooted on different features entirely. ExaBoost#21 stops CUDA training from aborting with an illegal memory access whenever bagging was on (bagging_fraction < 1, bagging_freq > 0) for any non-trivial dataset, tracked down to the out-of-bag score update.

The other two are the fiddly kind. ExaBoost#24 makes the CUDA best-split finder round leaf counts the way the CPU does — CUDA used __double2int_rn (round half to even) where Common::RoundInt is static_cast<int>(x + 0.5) (round half up), and the two disagree at exact .5 ties, which is enough to perturb reported leaf_count/internal_count for non-constant-hessian objectives. And ExaBoost#25 drops a full SynchronizeCUDADevice that sat on every node split: the two child leaves' SyncBestSplit reductions now run concurrently on separate streams instead of being serialized behind a host↔GPU round trip — still deterministic, since each leaf reads only its own range.

microsoft/onnxruntime

All three PRs here were about teaching the CoreML EP to own the ops that transformer attention-mask graphs actually emit. onnxruntime#28595 lets the ML Program Cast builder accept BOOL as a source and target dtype (the underlying cast op already handled it; only the input/output type gate omitted it) and moves the legacy "no preceding node" check after the ML Program early-return so a Cast fed directly by a graph input stops forcing a needless CPU fallback. onnxruntime#28598 adds a GatherND builder mapping to CoreML's gather_nd; since that op rejects a bool x but attention-mask graphs gather from bool tensors, it lowers the bool case as cast(bool→int32) → gather_nd → cast(int32→bool), which is lossless.

onnxruntime#28597 rounds it out with Where → ML Program select and And → a new logical_and builder, both ML-Program-only and both falling back to CPU on the NeuralNetwork format. It depends on the bool-Cast fix above: And is bool in and bool out, and a CoreML partition can't have bool I/O, so a meaningful test has to sandwich it between casts — which only works once bool Cast is supported.

exa-labs/exa-js

Two small ones, one real. exa-js#174 is the TypeScript half of a cross-SDK fix: a search monitor configured with an outputSchema returns output.content as a structured JSON object, but the type only allowed string | null, so it's widened to string | Record<string, unknown> | null to match the existing DeepSearchOutput.content convention. exa-js#175 is a pure release bump to 2.13.1 so that fix (and the earlier real-HTTP-error one) actually reach npm, where latest had been stuck at 2.13.0.

exa-labs/exa-py

exa-py#211 is the Python half of the same fix, and the more consequential one: SearchMonitorRunOutput.content was typed Optional[str], so retrieving a structured-output monitor run raised a pydantic ValidationError — the SDK rejecting the API's own response. One-line widening to Optional[Union[str, Dict[str, Any]]], mirroring the already-correct DeepSearchOutput.content.

That's June: GPUs made a little more deterministic, CoreML taught a handful of new ops for the attention-mask crowd, and two SDKs that finally stopped arguing with their own backend. See you in July.