fix(mlx): hide the unavailable MLX provider stub - #676
Merged
Conversation
The MLX provider is a stub: it has no bundled mlx-lm Python runtime and its
only run-fn throws on every call. It nevertheless inherited the default
`isAvailable()` of `true` and `registerMlx()` registered it unconditionally,
so `getProviders()` / `getInstalledProviderIds()` listed it and
`getProviderIdsForCapabilities(["text.generation"])` returned it — offering
users a text-generation provider whose every invocation fails.
`MlxProvider` now overrides `isAvailable()` to return false, and
`registerMlx()` consults it, warns, and returns without touching the
registry. The throwing run-fn stays as a backstop for anyone constructing
and registering the provider directly (the base class rejects a
registration with no run-fns), and all provider metadata is unchanged.
`registerMlx()`'s options parameter now defaults to `{}` so the call reads
as a probe rather than requiring arguments it will usually ignore.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PwyJuFrJnibKvrrk8Fa4Fn
Coverage Report
File CoverageNo changed files found. |
This was referenced Aug 5, 2026
Merged
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.
Closes #576
What changed and why
@workglow/mlxis a stub. It bundles no mlx-lm Python runtime, and its single registered run-fn throws"MLX provider not available: Python runtime not bundled."on every call. Despite that, it inheritedAiProvider's defaultisAvailable()oftrueandregisterMlx()registered it unconditionally — so after registration the registry reported it as a real, installed provider:getProvider("LOCAL_MLX")→ the provider instancegetInstalledProviderIds()→ includedLOCAL_MLXgetProviderIdsForCapabilities(["text.generation"])→ includedLOCAL_MLXThat last one is the one that bites: any UI or validation path asking "which providers can do text generation?" was offered a provider whose every invocation fails.
Two changes, both confined to
providers/mlx:MlxProvideroverridesisAvailable()to returnfalse. The seam already existed onAiProvider(chrome-aiis the only other override); MLX just wasn't using it. The stale class doc — which claimed the provider "registers cleanly so the UI can list it" — is corrected.registerMlx()consultsisAvailable(), warns, and returns without touching the registry. Itsoptionsparameter now defaults to{}so the call reads as a probe.The throwing run-fn is deliberately kept as a backstop:
AiProvider.register()rejects a registration with zero run-fns ("worker is required when no promiseRunFns are provided"), so the provider cannot be made capability-free, and anyone constructing + registering it directly still gets a clear immediate failure rather than silence. No new API was added topackages/ai, and no package was deleted.This changes what
registerMlx()does, not just what it reports. Any consumer (builder, sec, or other) that callsregisterMlx()and then expectsgetProvider("LOCAL_MLX")to be defined now getsundefined. The same applies togetInstalledProviderIds()andgetProviderIdsForCapabilities(), which no longer listLOCAL_MLX. Nothing inside this repo callsregisterMlx(), but@workglow/mlxis a published public package, so downstream code asserting on its presence must be updated. Awarn-level log line fires on every skipped registration, so the skip is visible rather than silent.Tests
New
packages/test/src/test/ai/MlxProvider.test.ts(6 tests, registry isolated per-test withsetAiProviderRegistry(new AiProviderRegistry())) covering:isAvailable()is false;registerMlx()resolves and registers nothing across all three listing surfaces; the direct-registration backstop still rejects with/Python runtime not bundled/; and a metadata guard onname/displayName/isLocal/supportsBrowser/supportsServer.Verification
All commands run from the branch, real output:
bun run buildTasks: 84 successful, 84 totalbun scripts/test.ts ai vitestTest Files 39 passed (39),Tests 253 passed (253)bun run build:types --forceTasks: 41 successful, 41 total,Cached: 0 cached(cache bypassed so this is a real compile)bun scripts/typecheck-budget.ts --json --no-buildtypecheck-budget: OK (38 packages within budget)npx prettier --check(3 changed files)All matched files use Prettier code style!eslint src --max-warnings 0inproviders/mlxNew test file in isolation:
The tests were confirmed to fail against the pre-fix code. Stashing only the two source changes and rebuilding produced exactly the bug's three assertions:
Typecheck budget:
providers/mlxmeasures 578 instantiations against a committed budget of 577. It is not gated — the budget file'sflooris 50000, three orders of magnitude above it — and the guard reports OK, so the committed value was left untouched rather than churned for a package the gate ignores.Follow-up (not fixed here)
chrome-aihas the same latent shape:WebBrowserProvideroverridesisAvailable()(probing for theLanguageModelglobal) butregisterWebBrowser()registers unconditionally without consulting it. Outside a Chrome build with the AI globals present, it lands in the registry exactly as MLX did. That is a separate behavior change with a real (non-stub) provider behind it and should get its own issue rather than riding along here.🤖 Generated with Claude Code
https://claude.ai/code/session_01PwyJuFrJnibKvrrk8Fa4Fn
Generated by Claude Code