This repository was archived by the owner on Jun 12, 2026. It is now read-only.
refactor: port MLX infrastructure directly from Python - #6
Merged
Merged
Conversation
- Update MLP generator to use vendored SwitchLayers classes - Add sanitize() with convertMoePackedTensors() for MoE models - Add mlxTopK helper for MoE routing - New flags: defaultRmsNormEps, defaultSlidingWindow, useTraditionalRope - GPT-OSS defaults: ropeTheta=150000, numExperts=128 - Vendor SwitchLayers.swift from mlx-swift-lm - Remove old MoELayers.swift - Update pre-push hook to regenerate all models including GPT-OSS - Regenerate all Swift model files
Port key infrastructure files directly from mlx-lm (Python) instead of mlx-swift-lm, as the Python version is more complete and frequently updated. Ported files: - KVCache.swift: KVCacheSimple, RotatingKVCache, QuantizedKVCache - RoPEUtils.swift: Llama3RoPE, YarnRoPE, SuScaledRoPE, initializeRope() - SwitchLayers.swift: SwitchLinear, QuantizedSwitchLinear, SwitchGLU, SwitchMLP - LLMModel.swift: Updated header, already complete New documentation: - PORTING_DECISIONS.md: Architectural decisions and rationale - .cursor/prompts/port-python-to-swift.md: Slash command for future porting Design decisions: - Protocol-based architecture (KVCache, RoPEProvider) - Focus on popular models (Llama, Qwen, Phi, Gemma, Mistral, GPT-OSS) - Skip niche features (batch processing, SSM models, prompt caching)
Add comprehensive tests for MoE infrastructure: - SwitchLinear: basic, no-bias, forward, sorted indices - QuantizedSwitchLinear: creation, forward pass - SwitchGLU: basic properties, forward pass - SwitchMLP: basic properties, forward pass - GPT-OSS SwiGLU: basic, clipping, compiled version - SwiGLUSwitchGLU: basic, forward pass - Helper functions: gatherSort, scatterUnsort
BREAKING CHANGE: Complete removal of all infrastructure code Deleted files: - AttentionUtils.swift - Generate.swift - KVCache.swift - LLMModel.swift - ModelLoader.swift - NodeMLXCore.swift - RoPEUtils.swift - StringOrNumber.swift - SwitchLayers.swift - Tokenizer.swift - Vision/* (VLM support) - All tests New directory structure: - /generated/models/ - Auto-generated model code (hf2swift) - /ported/ - Code ported from mlx-lm Python (LLM-assisted) - Root - Hand-written code This is a clean cut to enable fresh porting from mlx-lm (Python) with proper architecture and premium Swift quality.
Freshly ported from mlx-lm (Python), not from mlx-swift-lm: ported/KVCache.swift: - StandardKVCache: grow-in-place with step=256 - RotatingKVCache: sliding window with attention sinks - QuantizedKVCache: 8-bit quantized storage - KVCacheProtocol: common interface for all caches - createCausalMask/createAttentionMask utilities ported/RoPEUtils.swift: - RoPEProvider protocol for all RoPE variants - SuScaledRoPE (longrope) - Llama3RoPE (smooth frequency interpolation) - YarnRoPE (extended context) - StandardRoPE (wrapper for MLXNN.RoPE) - initializeRope() factory function ported/SwitchLayers.swift: - SwitchLinear/QuantizedSwitchLinear for MoE - SwitchGLU/SwitchMLP for expert computation - SwiGLUSwitchGLU for GPT-OSS with clipped activation - gatherSort/scatterUnsort helper functions Hand-written integration code: - LLMModel.swift: Protocol and ModelFactory - Generate.swift: Text generation loop - Tokenizer.swift: HFTokenizer wrapper - NodeMLXCore.swift: Main engine - StringOrNumber.swift: JSON decoding helper TODO: Align generated model APIs with new infrastructure
- Replace rope.apply() with rope() (callAsFunction) - Update createAttentionMask to use n: and offset: parameters - Add indices: label for SwitchGLU expert calls - Add SmolLM3 to naming special cases - Add state property to KVCacheProtocol for KV-sharing - Add GenerationResult and generateStream to LLMEngine - Add loadModel(modelId:) with HuggingFace Hub support - Regenerate all model files with new conventions
- Add KVCacheTests: StandardKVCache, RotatingKVCache, QuantizedKVCache - Add RoPEUtilsTests: StandardRoPE, Llama3RoPE, YarnRoPE, SuScaledRoPE - Add SwitchLayersTests: SwitchLinear, SwitchGLU, SwiGLUSwitchGLU, SwitchMLP Note: Tests require Apple Silicon with Metal GPU at runtime. MLX Metal library must be available for actual test execution.
- Add git hash 7585c142a6be9c9245f4ce61d087839776cb8275 (2026-01-12) to: - ported/KVCache.swift - ported/RoPEUtils.swift - ported/SwitchLayers.swift - Update ported/README.md with version info - Update PORTING_DECISIONS.md with current version - Update port-python-to-swift.md prompt with hash requirement
- Fix testCreateCausalMask: simplified assertion - Fix testQuantizedKVCacheUpdate: account for quantized dimensions - Fix testRotatingKVCacheRotation: offset can exceed maxSize - Fix testInitializeRopeSuScaled: use 'longrope' type - Fix SwitchLayers tests: use proper Int32 array initializers - Remove direct gatherSort/scatterUnsort tests (tested implicitly) All 49 tests now pass on M1 Mac Studio.
- Switch from xcodebuild to swift test for Swift Package - Copy mlx.metallib to test bundle before running tests - Remove code coverage setup (can be added later if needed)
- Update pre-push hook to use generated/models directory - Regenerate all models with latest generator output
- Add shared/ directory with reusable Swift components: - RMSNorm.swift: Universal RMSNorm implementation - Protocols.swift: BaseModelConfiguration protocol - StandardAttention.swift: Generic attention layer - StandardMLP.swift: SwiGLU MLP block - StandardDecoder.swift: Pre-norm decoder layer - WeightSanitizer.swift: Common weight sanitization - Update hf2swift generator: - Use typealias for RMSNorm in non-Gemma models - Use sanitizeWeights() for standard models - Reduces code duplication across generated models - Gemma models retain custom RMSNorm (1+weight scaling) - MoE models retain custom sanitize logic
- Add ported/GemmaRMSNorm.swift from mlx-lm gemma.py - Uses (1 + weight) scaling pattern specific to Gemma models - Generator now uses typealias for both standard and Gemma RMSNorm - Update PORTING_DECISIONS.md with GemmaRMSNorm section - All model-specific code now properly sourced from Python This follows the porting philosophy: Python → Swift with git hash tracking, not hardcoded patterns in the generator.
Split ModelFeatures into two tiers: 1. ArchitecturalFeatures - immutable per model family 2. ConfigValues - read from config.json with defaults Benefits: - Config values now come from config.json (source of truth) - Model-specific code paths are feature-driven - Reduced hardcoded values in the generator - getModelFeatures() accepts optional configJson parameter
- Add AttentionConfiguration protocol to Protocols.swift - Create FusedQKVAttention<C> generic class in shared/ - Update generator to produce typealias instead of 80 lines inline code - Phi3/Phi4 models now use: typealias Phi3Attention = FusedQKVAttention<Phi3Config> Generator savings: 74 lines removed from attention.ts Generated code: 80 lines → 3 lines per model using fused QKV Benefits: - Shared code is testable in isolation - Bug fixes apply to all models - Generator is simpler and more maintainable
- MoESanitizer: MoE weight sanitization (80 lines -> 4 lines) - MathUtils: erfinv() for gelu_topk threshold - SparseMLP: generic for sparse activation models - SparseMLPConfiguration protocol added
Generator now uses typealiases to shared components when possible: - StandardAttention<C> for basic attention - StandardMLP<C> for SiLU MLP - StandardDecoderLayer<C> for standard pre-norm decoder Llama/Qwen2 now generate: typealias LlamaAttention = StandardAttention<LlamaConfiguration> typealias LlamaMLP = StandardMLP<LlamaConfiguration> typealias LlamaDecoderLayer = StandardDecoderLayer<LlamaConfiguration> Benefits: - Generated code reduced from ~350 lines to ~195 lines - Complex logic now in testable shared components - Generator simplified with feature-based routing
Shared Components: - AltUpBlock<C>: Alternating Updates for efficient sparse computation - LaurelBlock<C>: Learned Augmented Residual for low-rank residuals - MathUtils.clipResidual(): Float16 overflow protection - MathUtils.topK(): Efficient top-k selection for MoE Generator Changes: - All Configurations now conform to BaseModelConfiguration - AltUpConfiguration, LaurelConfiguration protocols for Gemma3n - Utility functions delegate to MathUtils shared implementation Generated Code Reduction: - Gemma3n: -117 lines (-95 net) - All models: BaseModelConfiguration conformance moved to config struct This prepares for future models using similar architecture patterns.
New structure:
generator/
model-defs/
types.ts - Interfaces + defaults
llama.ts - Llama family
qwen.ts - Qwen2, Qwen3
gemma.ts - Gemma3, Gemma3n
phi.ts - Phi3, Phi4
mistral.ts - Mistral, Mistral3
gpt-oss.ts - GPT-OSS MoE
smollm.ts - SmolLM3
index.ts - Registry + exports
features.ts - Now just orchestration (~100 lines)
Benefits:
- features.ts reduced from 466 to 100 lines
- Each model family isolated in own file
- Easy to add new models (just create new file + register)
- Model definitions are self-documenting with JSDoc
Updated documentation to reflect the refactored codebase: PORTING_DECISIONS.md: - Complete rewrite with architecture overview - Added shared components section - Added generator architecture section - Removed obsolete TODO section - Added version history NodeMLXCore/README.md: - Three-layer architecture diagram - Edit policy table - Supported models table - Links to subdirectory READMEs shared/README.md: - Complete protocol catalog - Standard vs. specialized components - Usage examples in generated code - Adding new components guide ported/README.md: - Source tracking with git hash - Porting guidelines - Update process - Design decisions reference generated/README.md: - Model table with features - Regeneration commands - Generator source structure - Validation process - Adding new models guide NEW: packages/hf2swift/README.md: - Complete generator documentation - Architecture overview - Model definitions guide - Feature flags reference - Development commands .cursor/prompts/port-python-to-swift.md: - Updated file locations - Added shared components section - Improved workflow - Added documentation update checklist
New test files ported from mlx-lm Python test suite: - SamplingUtilsTests: top-p, top-k, min-p sampling (from test_sample_utils.py) - MaskTests: causal mask creation with windows (from test_models.py) - GenerateTests: generation config and token sampling New implementation: - SamplingUtils.swift: Public sampling utilities (applyTopP, applyTopK, applyMinP) All 81 tests pass on Apple Silicon.
- Change var to let where variables aren't mutated (Generate.swift, RoPEUtils.swift) - Remove unnecessary try expression (NodeMLXCore.swift) - Bump Swift cache key to v2 to invalidate stale cache - Include Swift source files in cache hash - Fix metallib path for test bundle
Welcome to Codecov 🎉Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests. Thanks for integrating Codecov - We've got you covered ☂️ |
- Add StringOrNumberTests for JSON type handling - Add PerformanceTests for MLX operations (attention, RoPE, KVCache) - Add IntegrationTests for real model loading/generation - Update CI to run unit tests only (integration via smoke test) - Total: 98 unit tests passing
This branch was previously deployed
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
Summary
Switches the MLX Swift infrastructure from ad-hoc implementations to systematic ports from Apple's
mlx-lmPython library. This provides access to the latest model architectures faster and ensures compatibility with the canonical MLX implementation.Key Changes
Architecture Decisions
Why port from Python instead of mlx-swift-lm?
mlx-lm(Python) is Apple's primary implementation, updated more frequently, and supports models like Llama 4 MoE beforemlx-swift-lmcatches up. Direct porting gives us full control over the timeline.Directory structure
What was ported
cache.pyrope_utils.pyswitch_layers.pyWhat was intentionally skipped
Batch processing (server use case), SSM/Mamba support, prompt cache serialization — these can be added when needed.
Reference
7585c142a6be9c9245f4ce61d087839776cb8275.cursor/prompts/port-python-to-swift.mdpackages/swift/PORTING_DECISIONS.md