fix(mlx-worker): ship mlx.metallib and keep MLX errors off the protocol channel - #47
Open
zxch3n wants to merge 1 commit into
Open
fix(mlx-worker): ship mlx.metallib and keep MLX errors off the protocol channel#47zxch3n wants to merge 1 commit into
zxch3n wants to merge 1 commit into
Conversation
…ol channel The MLX worker never worked from `swift build` output: command-line SwiftPM cannot compile Metal shaders (xcodebuild-only, per the mlx-swift README), so no default.metallib existed anywhere and the first MLX call died in Device::Device with "Failed to load the default metallib". mlx-c's default error handler then printed `MLX error: ...` to stdout — the worker's line-delimited JSON channel — and exit(-1)'d, which afterrayd reported as "MLX worker wrote non-protocol stdout". - scripts/build-mlx-metallib.sh compiles the checkout's generated shaders (same flags as mlx's CMake kernel build) into mlx.metallib and colocates it with the built worker binaries, where mlx's load order looks first. run-v0.sh / build-release.sh call it after building the worker. In the app bundle the library lives in Contents/Resources behind a Helpers symlink — a data file inside Helpers fails codesign --verify --deep --strict. - installMlxErrorHandler() installs a process-wide mlx-c error sink that logs to stderr before exiting; scoped `withError` handling is unaffected. Verified: new worker-core tests (metallib loads the Metal default library; unscoped broadcast error reaches the sink), protocol smoke test on the signed bundled worker, make test, and run-v0.sh --build-only including codesign verification. Not verified: real-model generation — no verified Qwen3.5 snapshot on this machine yet (4B download still partial). Model: kimi-code/kimi-for-coding Harness: lody
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The MLX VLM worker could never start from
swift buildoutput. Command-line SwiftPM cannot compile Metal shaders (xcodebuild-only, per the mlx-swift README), so nodefault.metallibexisted anywhere in.build. The worker's first MLX call died inDevice::Devicewith "Failed to load the default metallib", and mlx-c's default error handler printedMLX error: ...to stdout — the worker's line-delimited JSON protocol channel — before callingexit(-1). afterrayd surfaced this asmodel worker returned invalid output: MLX worker wrote non-protocol stdout, and the app fell back to "language model is not configured".Changes
scripts/build-mlx-metallib.sh(new): compiles the mlx-swift checkout's generated Metal shaders intomlx.metallibwith the same flags as mlx's CMake kernel build, then colocates it with the built worker binaries — the first place mlx'sload_colocated_librarylooks. Rebuilds only when shaders orPackage.resolvedchange. Preflights the Xcode 26+ Metal component with a clear remedy (xcodebuild -downloadComponent MetalToolchain).scripts/run-v0.sh/scripts/build-release.sh: call the script after buildingafterray-mlx-vlm-workerand ship the library in the app bundle. It lives inContents/Resources/mlx.metallibbehind aContents/Helpers/mlx.metallibsymlink, because a data file directly inside Helpers failscodesign --verify --deep --strictas unsigned nested code (verified empirically).swift/AfterRayMlxVlmWorker/Sources/WorkerCore.swift:installMlxErrorHandler()installs a process-wide mlx-c error sink that logs to stderr and exits, instead of mlx-c's default stdout printf. Uses the deprecatedMLX.setErrorHandlerdeliberately — it is mlx-swift's only process-wide hook; task-scopedwithError/withErrorHandlerstill take precedence and are unaffected. The executable installs it at startup.MlxWorkerProcessSafetyTests): (1) an unscoped MLX broadcast error reaches the installed sink — previously it would have hit stdout and killed the process; (2) the colocatedmlx.metallibactually loads the Metal default library and runs a GPU op (skipped with instructions when the script has not run).scripts/,swift/AfterRayMlxVlmWorker/,apps/AfterRayMlxVlmWorker/.Verification
swift test --filter MlxWorkerProcessSafetyTests— both new tests pass.generatewithout load, malformed line, badmodel_dir): stdout stays pure JSON, logs on stderr.scripts/run-v0.sh --build-only— full dev build, bundle assembly, andcodesign --verify --deep --strictall pass with the metallib included.make test(cargo workspace + swift test + capture shim) passes.cargo clippy --workspace --all-targets -- -D warningsis already red onmain(cast_sign_lossincrates/afterray-codec/src/jpeg.rs). This PR touches no Rust code.Confidence
Medium-high. Both root causes are verified at the mechanism level with tests and a full signed dev build. The release pipeline change mirrors the dev flow but
build-release.shitself was not run (needs notarization credentials); notarization of the symlinked resource layout follows standard bundle rules but has not been exercised.