Skip to content

Commit d712f06

Browse files
authored
Merge pull request #2 from SharpAI/feat/add-ci-sync-workflows
feat: Integrates Upstream Sync & Fork Protection
2 parents 6d3a11f + 69c05cc commit d712f06

4 files changed

Lines changed: 193 additions & 0 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Downstream Integration Test
2+
3+
on:
4+
pull_request:
5+
branches: [ main ]
6+
7+
jobs:
8+
test-swiftlm-umbrella:
9+
name: Test with SwiftLM (Umbrella Repo)
10+
runs-on: macos-15
11+
timeout-minutes: 40
12+
steps:
13+
- name: Checkout PR branch (mlx-swift)
14+
uses: actions/checkout@v4
15+
with:
16+
fetch-depth: 0
17+
path: mlx-swift-pr
18+
19+
- name: Checkout SwiftLM (Parent Repo)
20+
uses: actions/checkout@v4
21+
with:
22+
repository: SharpAI/SwiftLM
23+
path: SwiftLM
24+
token: ${{ secrets.GITHUB_TOKEN }}
25+
26+
- name: Install Metal Toolchain
27+
run: xcodebuild -downloadComponent MetalToolchain || true
28+
29+
- name: Resolve Dependencies (to populate SPM cache)
30+
run: swift package resolve
31+
working-directory: SwiftLM
32+
33+
- name: Override mlx-swift dependency with PR checkout
34+
# SPM `package edit` puts a local editable copy of the package in
35+
# .build/checkouts; we then replace its contents with our PR branch.
36+
run: |
37+
swift package edit mlx-swift --path $GITHUB_WORKSPACE/mlx-swift-pr
38+
working-directory: SwiftLM
39+
40+
- name: Build SwiftLM
41+
run: swift build --build-tests
42+
working-directory: SwiftLM
43+
44+
- name: Install MLX Metal library
45+
run: |
46+
python3 -m venv /tmp/mlx_venv
47+
/tmp/mlx_venv/bin/pip install --quiet mlx
48+
METALLIB=/tmp/mlx_venv/lib/python*/site-packages/mlx/lib/mlx.metallib
49+
# Copy to all locations the test runner and linked bundles may search
50+
cp $METALLIB SwiftLM/.build/debug/ || true
51+
find SwiftLM/.build -name "*.xctest" -exec cp $METALLIB {}/Contents/MacOS/ \; 2>/dev/null || true
52+
find SwiftLM/.build -type d \( -name "debug" -o -name "release" \) -exec cp $METALLIB {}/ \; 2>/dev/null || true
53+
# Also copy beside test binaries directly
54+
find SwiftLM/.build -name "*Tests" -type f -exec sh -c 'cp '$METALLIB' "$(dirname {})"/' \; 2>/dev/null || true
55+
56+
- name: Run SwiftLM Integration Tests
57+
run: swift test --parallel --skip-build
58+
working-directory: SwiftLM
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: Upstream Sync
2+
3+
on:
4+
schedule:
5+
- cron: '0 */4 * * *' # Run every 4 hours
6+
workflow_dispatch: # Allow manual triggering
7+
8+
jobs:
9+
check-and-create-pr:
10+
name: Check Upstream and Create PR
11+
runs-on: ubuntu-latest
12+
permissions:
13+
contents: write
14+
pull-requests: write
15+
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0 # Required to fetch all history for commit comparison
21+
22+
- name: Check for upstream changes and set up branch
23+
id: check_upstream
24+
run: |
25+
# Add the upstream repository
26+
git remote add upstream https://github.com/ml-explore/mlx-swift.git
27+
git fetch upstream main
28+
29+
# Check how many commits upstream is ahead of our local main
30+
AHEAD=$(git rev-list --count HEAD..upstream/main)
31+
echo "Upstream is $AHEAD commits ahead."
32+
33+
if [ "$AHEAD" -gt 0 ]; then
34+
echo "has_changes=true" >> $GITHUB_OUTPUT
35+
BRANCH_NAME="sync/upstream-latest"
36+
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
37+
38+
# Checkout upstream main into the static sync branch (force reset if it exists)
39+
git checkout -B $BRANCH_NAME upstream/main
40+
git push --force origin $BRANCH_NAME
41+
else
42+
echo "has_changes=false" >> $GITHUB_OUTPUT
43+
echo "No new commits to sync."
44+
fi
45+
46+
- name: Create or Update Pull Request
47+
if: steps.check_upstream.outputs.has_changes == 'true'
48+
run: |
49+
# Check if a PR already exists for this branch
50+
PR_EXISTS=$(gh pr list HEAD --head ${{ steps.check_upstream.outputs.branch_name }} --json number --jq 'length')
51+
52+
if [ "$PR_EXISTS" -eq "0" ]; then
53+
gh pr create \
54+
--title "🔄 Auto-Sync: Upstream changes from ml-explore/mlx-swift" \
55+
--body "Automated PR to sync the latest changes from upstream \`ml-explore/mlx-swift\`'s \`main\` branch.
56+
57+
### Merge Warnings
58+
* **Do NOT blind merge!** This repo contains custom SharpAI out-of-core SSD features.
59+
* Refer to the \`UPSTREAM_MERGE_PLAN.md\` file locally to check standard SharpAI metal optimization discrepancies.
60+
* Do **not** overwrite the submodule branch pointers under \`Package.swift\` without consultation." \
61+
--base main \
62+
--head ${{ steps.check_upstream.outputs.branch_name }}
63+
else
64+
echo "PR already exists. Force pushing to the branch updated the PR automatically."
65+
fi
66+
env:
67+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import XCTest
2+
import Foundation
3+
4+
final class ForkProtectionTests: XCTestCase {
5+
6+
/// This test ensures that the SharpAI custom SSD streaming kernel
7+
/// (`moe_stream.metal`) is natively preserved in the generated metal code and sources.
8+
/// If an upstream merge resets the metal codegen generator, this will safely fail.
9+
func testSSDStreamerMetalCodegenExists() throws {
10+
// Look for the metal source in the parent package structure to verify the submodule holds our patches
11+
let fileManager = FileManager.default
12+
let packageRoot = URL(fileURLWithPath: #file)
13+
.deletingLastPathComponent()
14+
.deletingLastPathComponent()
15+
.deletingLastPathComponent()
16+
17+
let customKernelPath = packageRoot
18+
.appendingPathComponent("Source/Cmlx/mlx/mlx/backend/metal/kernels/moe_stream.metal")
19+
20+
XCTAssertTrue(fileManager.fileExists(atPath: customKernelPath.path),
21+
"🚨 ALARM: The SharpAI custom ssd-stream metal kernel is missing! A blind upstream Apple merge erased moe_stream.metal! Do not merge this PR.")
22+
}
23+
24+
/// This test verifies that the custom ThreadPool modification we added to load.cpp is still tracked.
25+
func testThreadPoolFeatureExists() throws {
26+
let fileManager = FileManager.default
27+
let packageRoot = URL(fileURLWithPath: #file).deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent()
28+
29+
let threadPoolPath = packageRoot.appendingPathComponent("Source/Cmlx/mlx/mlx/threadpool.h")
30+
XCTAssertTrue(fileManager.fileExists(atPath: threadPoolPath.path),
31+
"🚨 ALARM: The SharpAI custom ThreadPool is missing! Did the upstream ml-explore merge drop the background loader?")
32+
}
33+
}

UPSTREAM_MERGE_PLAN.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# SharpAI `mlx-swift` Custom Patches & Upstream Merge Guide
2+
3+
This document captures the divergence between the `SharpAI/mlx-swift` custom fork and the official `ml-explore/mlx-swift` upstream repository. **Do not overwrite these commits or configurations** during an upstream merge unless Apple officially merges the underlying features.
4+
5+
These patches enable out-of-core model inference, specifically for SSD Flash Streaming, by relying on custom bindings and our dedicated `SharpAI/mlx` and `SharpAI/mlx-c` forks.
6+
7+
### 1. Speculative Decoding
8+
_Note: Core speculative decoding logic (Drafting, KV Cache routing) primarily exists natively in `SwiftBuddy`/`mlx-swift-lm`. Modifications inside `mlx-swift` strictly pertain to the underlying memory/cache alignment requirements (like bounds limit resolutions) rather than the decoding algorithms themselves._
9+
10+
### 2. SSD Streaming & Out-of-core Patches
11+
Our core optimizations extend the native Engine bindings to allow pulling model matrices from disk to GPU memory directly.
12+
- `6d3a11f` / `9e10176`: **SSD thread-pooling** - Implements background thread-pools in the wrappers with dynamic API toggles to manage off-main-thread I/O operations.
13+
- `14f267b`: **GLM-5.1 compatibility limit fix** - Removes obsolete buffer bounds checks in the `ssd_streamer` that originally crashed multi-expert GLM instances.
14+
- `8540be8` / `877d992`: **C-API extensions** - Restores modifications to the Swift bridge and `fast.h` to explicitly surface C++ streaming kernels (`moe_stream_op`).
15+
- `b45b31c`: **Gemma 4 Apple Silicon array mapping** - Customized Metal kernels optimized for Gemma 4 memory configurations.
16+
17+
### 3. Metal Code Generation & JIT Extensions
18+
Because Swift package compilation automatically generates C++ metal headers, the JIT builder was intercepted to include our custom kernel `.metal` files.
19+
- `61590a5`: Adds the custom `moe_stream.metal` processing logic directly into the codegen workflow.
20+
- `7bb740b`: Tracks the SharpAI custom metal kernels alongside Apple originals so the JIT builder doesn't ignore them.
21+
- `cbbf55c`: Enforces that the `fast.h` method signatures exactly match the upstreamed `fast.cpp` limits after code generation.
22+
- `441ef64`: Adds `steel_conv_3d` C++ string targeting internally for the `Cmlx` build step.
23+
24+
### 4. Build Configuration & Submodule Pointers
25+
The `Package.swift` and internal git components have been locked to point to our ecosystem. Overwriting these with Apple's pointers will break the compiler.
26+
- `2f60e7b`: Re-points Xcode and SPM to `SharpAI/mlx` and `SharpAI/mlx-c` dedicated GitHub forks instead of `ml-explore`.
27+
- `72daf6c` / `d0d852f`: Retains specific submodule inline configurations holding metadata/device streams specifically built to support the SharpAI metal optimizations.
28+
- `6139f94` / `41c8c4f`: Explicit `mlx-c` pointer bumps required to maintain compilation signatures for `fftshift` and `Shape`.
29+
- `a4b0d27`: Manual override bumping the `cxxLanguageStandard` parameter directly to `.gnucxx20` to guarantee C++ compatibility parity.
30+
31+
---
32+
### When merging upstream...
33+
1. Execute `git fetch upstream` (where upstream is `https://github.com/ml-explore/mlx-swift.git`).
34+
2. Do **not** use `--strategy-option=theirs` blindly!
35+
3. Any conflicts in `Package.swift` (submodule references), `Cmlx/`, or `Metal/` codegen generators MUST favor the **SharpAI local implementation (HEAD)** to prevent kernel erasure.

0 commit comments

Comments
 (0)