First — thanks for the v3 decoupling work in #118; the tokenizer/downloader seams are exactly what downstream SDKs need.
One follow-up cost we measured: the MLXHuggingFace product's only content is freestanding convenience macros (#huggingFaceTokenizerLoader(), #hubDownloader(), …), implemented in the MLXHuggingFaceMacros macro target. Depending on the product therefore compiles swift-syntax in every consumer graph.
Measured impact (ManifoldKit, an Apple-platforms LLM SDK wrapping MLXLLM/MLXVLM): in our default build the macro chain accounted for 103 swift-syntax compile tasks, 248 build tasks total, and ~24% of build CPU (794.7s → 601.8s user after removal). We worked around it by hand-inlining the macro expansion and dropping the product: roryford/ManifoldKit#1746
The expansions don't depend on the call site. TokenizerLoaderMacro/TokenizerAdaptorMacro/DownloaderMacro each expand to a fixed bridge type (TransformersLoader, TokenizerBridge, HubBridge). Ordinary public types would provide identical functionality with no swift-syntax involvement, e.g.:
// instead of: using: #huggingFaceTokenizerLoader()
using: HuggingFaceTokenizerLoader()
Suggestions (any subset helps):
- Export the bridge types as plain public API in
MLXHuggingFace (macros could remain as sugar, or be deprecated).
- If the macros stay, consider raising the swift-syntax floor to
602.0.0 — swift.org's signed prebuilts are keyed per swift-syntax tag + exact toolchain, and 600.0.1/601.0.1 artifacts are no longer published for current toolchains (verified: on Swift 6.3.2 / Xcode 26.5, a 600.x resolution silently falls back to the full ~212-task source compile, while 603.0.1 resolves to a prebuilt and builds in seconds). A downstream package pinning 600.x also drags the unified resolution below the prebuilt floor for everyone in the graph.
- Alternatively, move the macro layer into a separate opt-in product so
MLXHuggingFace consumers who only need the bridges don't carry the macro target.
Happy to open a PR for option 1 (plain bridge types + macro deprecation) if that direction is agreeable.
First — thanks for the v3 decoupling work in #118; the tokenizer/downloader seams are exactly what downstream SDKs need.
One follow-up cost we measured: the
MLXHuggingFaceproduct's only content is freestanding convenience macros (#huggingFaceTokenizerLoader(),#hubDownloader(), …), implemented in theMLXHuggingFaceMacrosmacro target. Depending on the product therefore compiles swift-syntax in every consumer graph.Measured impact (ManifoldKit, an Apple-platforms LLM SDK wrapping MLXLLM/MLXVLM): in our default build the macro chain accounted for 103 swift-syntax compile tasks, 248 build tasks total, and ~24% of build CPU (794.7s → 601.8s user after removal). We worked around it by hand-inlining the macro expansion and dropping the product: roryford/ManifoldKit#1746
The expansions don't depend on the call site.
TokenizerLoaderMacro/TokenizerAdaptorMacro/DownloaderMacroeach expand to a fixed bridge type (TransformersLoader,TokenizerBridge,HubBridge). Ordinary public types would provide identical functionality with no swift-syntax involvement, e.g.:Suggestions (any subset helps):
MLXHuggingFace(macros could remain as sugar, or be deprecated).602.0.0— swift.org's signed prebuilts are keyed per swift-syntax tag + exact toolchain, and 600.0.1/601.0.1 artifacts are no longer published for current toolchains (verified: on Swift 6.3.2 / Xcode 26.5, a 600.x resolution silently falls back to the full ~212-task source compile, while 603.0.1 resolves to a prebuilt and builds in seconds). A downstream package pinning 600.x also drags the unified resolution below the prebuilt floor for everyone in the graph.MLXHuggingFaceconsumers who only need the bridges don't carry the macro target.Happy to open a PR for option 1 (plain bridge types + macro deprecation) if that direction is agreeable.