diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 029c1a3..4507e00 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,10 +23,10 @@ jobs: run: dotnet --info - name: Restore dependencies - run: dotnet restore MLXSharp.sln + run: dotnet restore MLXSharp.slnx - name: Build C# projects (initial validation) - run: dotnet build MLXSharp.sln --configuration Release --no-restore + run: dotnet build MLXSharp.slnx --configuration Release --no-restore - name: Install CMake run: brew install cmake @@ -41,7 +41,7 @@ jobs: ls -la src/MLXSharp.Native/runtimes/osx-arm64/native/ - name: Rebuild solution with native libraries - run: dotnet build MLXSharp.sln --configuration Release --no-restore + run: dotnet build MLXSharp.slnx --configuration Release --no-restore - name: Setup Python for HuggingFace uses: actions/setup-python@v5 @@ -66,7 +66,7 @@ jobs: ls -la "$TEST_OUTPUT/runtimes/osx-arm64/native/" - name: Run tests - run: dotnet test MLXSharp.sln --configuration Release --no-build --logger "trx;LogFileName=TestResults.trx" --results-directory artifacts/test-results + run: dotnet test MLXSharp.slnx --configuration Release --no-build --logger "trx;LogFileName=TestResults.trx" --results-directory artifacts/test-results env: MLXSHARP_MODEL_PATH: ${{ github.workspace }}/models/Qwen1.5-0.5B-Chat-4bit @@ -78,22 +78,42 @@ jobs: cp src/MLXSharp.Native/runtimes/osx-arm64/native/libmlxsharp.dylib artifacts/native/ - name: Pack MLXSharp library - run: dotnet pack src/MLXSharp/MLXSharp.csproj --configuration Release --output artifacts/packages \ - -p:MLXSharpMacNativeBinary=$GITHUB_WORKSPACE/native/build/libmlxsharp.dylib + run: dotnet pack src/MLXSharp/MLXSharp.csproj --configuration Release --output artifacts/packages -p:MLXSharpMacNativeBinary=$GITHUB_WORKSPACE/native/build/libmlxsharp.dylib - name: Pack MLXSharp.SemanticKernel library - run: dotnet pack src/MLXSharp.SemanticKernel/MLXSharp.SemanticKernel.csproj --configuration Release --output artifacts/packages \ - -p:MLXSharpMacNativeBinary=$GITHUB_WORKSPACE/native/build/libmlxsharp.dylib + run: dotnet pack src/MLXSharp.SemanticKernel/MLXSharp.SemanticKernel.csproj --configuration Release --output artifacts/packages -p:MLXSharpMacNativeBinary=$GITHUB_WORKSPACE/native/build/libmlxsharp.dylib - name: Verify package contains native libraries run: | echo "Checking package contents..." - if unzip -l artifacts/packages/*.nupkg | grep -q "runtimes/osx-arm64/native/libmlxsharp.dylib"; then - echo "✓ Native library found in package" - else - echo "✗ ERROR: Native library NOT found in package!" - echo "Package contents:" - unzip -l artifacts/packages/*.nupkg + shopt -s nullglob + packages=(artifacts/packages/*.nupkg) + if [ ${#packages[@]} -eq 0 ]; then + echo "✗ ERROR: No packages were produced" + exit 1 + fi + + missing=0 + for package in "${packages[@]}"; do + echo "Inspecting ${package}" + filename=$(basename "${package}") + case "${filename}" in + MLXSharp.*.nupkg) + if unzip -l "${package}" | grep -q "runtimes/osx-arm64/native/libmlxsharp.dylib"; then + echo " ✓ Native library found" + else + echo " ✗ Native library NOT found" + unzip -l "${package}" + missing=1 + fi + ;; + *) + echo " ↷ Skipping native check for ${filename}" + ;; + esac + done + + if [ $missing -ne 0 ]; then exit 1 fi diff --git a/MLXSharp.sln b/MLXSharp.sln deleted file mode 100644 index 11abacc..0000000 --- a/MLXSharp.sln +++ /dev/null @@ -1,84 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLXSharp", "src\MLXSharp\MLXSharp.csproj", "{69BB544C-9ADD-4561-8B9D-7ACED3B46296}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLXSharp.Tests", "src\MLXSharp.Tests\MLXSharp.Tests.csproj", "{8F2FA235-E636-4D11-A55D-450505BB8F19}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLXSharp.Native", "src\MLXSharp.Native\MLXSharp.Native.csproj", "{F5B45C67-1810-4A04-862F-C33A7DFAE4C2}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLXSharp.SemanticKernel", "src\MLXSharp.SemanticKernel\MLXSharp.SemanticKernel.csproj", "{BF201BBA-D607-4EF5-BF9B-766D89B43A4A}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Debug|Any CPU.Build.0 = Debug|Any CPU - {69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Debug|x64.ActiveCfg = Debug|Any CPU - {69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Debug|x64.Build.0 = Debug|Any CPU - {69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Debug|x86.ActiveCfg = Debug|Any CPU - {69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Debug|x86.Build.0 = Debug|Any CPU - {69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Release|Any CPU.ActiveCfg = Release|Any CPU - {69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Release|Any CPU.Build.0 = Release|Any CPU - {69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Release|x64.ActiveCfg = Release|Any CPU - {69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Release|x64.Build.0 = Release|Any CPU - {69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Release|x86.ActiveCfg = Release|Any CPU - {69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Release|x86.Build.0 = Release|Any CPU - {8F2FA235-E636-4D11-A55D-450505BB8F19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8F2FA235-E636-4D11-A55D-450505BB8F19}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8F2FA235-E636-4D11-A55D-450505BB8F19}.Debug|x64.ActiveCfg = Debug|Any CPU - {8F2FA235-E636-4D11-A55D-450505BB8F19}.Debug|x64.Build.0 = Debug|Any CPU - {8F2FA235-E636-4D11-A55D-450505BB8F19}.Debug|x86.ActiveCfg = Debug|Any CPU - {8F2FA235-E636-4D11-A55D-450505BB8F19}.Debug|x86.Build.0 = Debug|Any CPU - {8F2FA235-E636-4D11-A55D-450505BB8F19}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8F2FA235-E636-4D11-A55D-450505BB8F19}.Release|Any CPU.Build.0 = Release|Any CPU - {8F2FA235-E636-4D11-A55D-450505BB8F19}.Release|x64.ActiveCfg = Release|Any CPU - {8F2FA235-E636-4D11-A55D-450505BB8F19}.Release|x64.Build.0 = Release|Any CPU - {8F2FA235-E636-4D11-A55D-450505BB8F19}.Release|x86.ActiveCfg = Release|Any CPU - {8F2FA235-E636-4D11-A55D-450505BB8F19}.Release|x86.Build.0 = Release|Any CPU - {F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Debug|x64.ActiveCfg = Debug|Any CPU - {F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Debug|x64.Build.0 = Debug|Any CPU - {F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Debug|x86.ActiveCfg = Debug|Any CPU - {F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Debug|x86.Build.0 = Debug|Any CPU - {F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Release|Any CPU.Build.0 = Release|Any CPU - {F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Release|x64.ActiveCfg = Release|Any CPU - {F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Release|x64.Build.0 = Release|Any CPU - {F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Release|x86.ActiveCfg = Release|Any CPU - {F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Release|x86.Build.0 = Release|Any CPU - {BF201BBA-D607-4EF5-BF9B-766D89B43A4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BF201BBA-D607-4EF5-BF9B-766D89B43A4A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BF201BBA-D607-4EF5-BF9B-766D89B43A4A}.Debug|x64.ActiveCfg = Debug|Any CPU - {BF201BBA-D607-4EF5-BF9B-766D89B43A4A}.Debug|x64.Build.0 = Debug|Any CPU - {BF201BBA-D607-4EF5-BF9B-766D89B43A4A}.Debug|x86.ActiveCfg = Debug|Any CPU - {BF201BBA-D607-4EF5-BF9B-766D89B43A4A}.Debug|x86.Build.0 = Debug|Any CPU - {BF201BBA-D607-4EF5-BF9B-766D89B43A4A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BF201BBA-D607-4EF5-BF9B-766D89B43A4A}.Release|Any CPU.Build.0 = Release|Any CPU - {BF201BBA-D607-4EF5-BF9B-766D89B43A4A}.Release|x64.ActiveCfg = Release|Any CPU - {BF201BBA-D607-4EF5-BF9B-766D89B43A4A}.Release|x64.Build.0 = Release|Any CPU - {BF201BBA-D607-4EF5-BF9B-766D89B43A4A}.Release|x86.ActiveCfg = Release|Any CPU - {BF201BBA-D607-4EF5-BF9B-766D89B43A4A}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {69BB544C-9ADD-4561-8B9D-7ACED3B46296} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - {8F2FA235-E636-4D11-A55D-450505BB8F19} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - {F5B45C67-1810-4A04-862F-C33A7DFAE4C2} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - {BF201BBA-D607-4EF5-BF9B-766D89B43A4A} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - EndGlobalSection -EndGlobal diff --git a/MLXSharp.slnx b/MLXSharp.slnx new file mode 100644 index 0000000..07b5992 --- /dev/null +++ b/MLXSharp.slnx @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/README.md b/README.md index 041bea6..e307ba0 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ The design mirrors the packaging approach from projects such as [LLamaSharp](htt - `IChatClient`, `IEmbeddingGenerator`, and image generation helpers that adhere to the `Microsoft.Extensions.AI` abstractions. - Builder-based backend configuration with a deterministic managed implementation for tests and a P/Invoke powered native backend. - Native library resolver that probes application directories, `MLXSHARP_LIBRARY`, or packaged runtimes and loads `libmlxsharp` on demand. -- `MLXSharp.Native` packaging project that ships stub binaries for CI (Linux x64 today) and a placeholder `osx-arm64` folder for the production MLX wrapper. - Dependency injection extensions (`AddMlx`) in **MLXSharp** package. - Semantic Kernel integration (`AddMlxChatCompletion`) in separate **MLXSharp.SemanticKernel** package. - Integration test suite that exercises chat, embedding, image, and Semantic Kernel flows against both managed and native backends. @@ -19,8 +18,8 @@ The design mirrors the packaging approach from projects such as [LLamaSharp](htt ├── extern/mlx # Git submodule with the official MLX sources ├── native/ # Native wrapper scaffold (CMake project) ├── src/MLXSharp/ # Managed library with Microsoft.Extensions.AI adapters +├── src/MLXSharp.Native/ # Native runtime packaging project with stub binaries ├── src/MLXSharp.SemanticKernel/ # Semantic Kernel integration (separate package) -├── src/MLXSharp.Native/ # NuGet-ready container for native binaries └── src/MLXSharp.Tests/ # Integration tests covering DI and Semantic Kernel ``` @@ -81,10 +80,12 @@ dotnet add package MLXSharp This package contains: - Managed DLL with `Microsoft.Extensions.AI` implementations -- Native libraries in `runtimes/{rid}/native/`: - - `runtimes/linux-x64/native/libmlxsharp.so` - stub for CI/testing +- Native assets in `runtimes/{rid}/native/`: + - `runtimes/linux-x64/native/libmlxsharp.so.b64` - Base64-encoded stub that `MlxNativeLibrary` expands for CI/testing - `runtimes/osx-arm64/native/libmlxsharp.dylib` - built in CI on macOS +`MlxNativeLibrary` materialises `libmlxsharp.so` from the encoded payload on first use so Git history stays free of binary blobs while tests retain deterministic behaviour. + ### MLXSharp.SemanticKernel [![NuGet](https://img.shields.io/nuget/v/MLXSharp.SemanticKernel.svg)](https://www.nuget.org/packages/MLXSharp.SemanticKernel) Semantic Kernel integration: diff --git a/src/MLXSharp.Native/README.md b/src/MLXSharp.Native/README.md index 195db0a..590cbb6 100644 --- a/src/MLXSharp.Native/README.md +++ b/src/MLXSharp.Native/README.md @@ -1,7 +1,7 @@ # MLXSharp.Native -This package mirrors the layout that MLXSharp expects when resolving native binaries. -It ships the `libmlxsharp` stub that our managed layer uses during development and in automated tests. -The Linux stub lives as a Base64-encoded payload (`libmlxsharp.so.b64`) so we can keep Git history free of binary blobs. -`MlxNativeLibrary` expands the encoded payload to `libmlxsharp.so` automatically the first time the native backend loads. -Drop the macOS build of the wrapper into `runtimes/osx-arm64/native/` before publishing a release build. +This helper project mirrors the runtime layout that MLXSharp expects when probing for native binaries. +It ships the stubbed `libmlxsharp` artefacts that our managed layer exercises during development and CI runs. +The Linux build lives as a Base64-encoded payload (`libmlxsharp.so.b64`) so Git history stays free of large binary blobs. +`MlxNativeLibrary` expands that payload to `libmlxsharp.so` on demand, while macOS builds should drop the compiled +`libmlxsharp.dylib` into `runtimes/osx-arm64/native/` before packing a release. diff --git a/src/MLXSharp.Native/runtimes/osx-arm64/native/README.txt b/src/MLXSharp.Native/runtimes/osx-arm64/native/README.txt index f1accc5..07e6d0b 100644 --- a/src/MLXSharp.Native/runtimes/osx-arm64/native/README.txt +++ b/src/MLXSharp.Native/runtimes/osx-arm64/native/README.txt @@ -1 +1 @@ -Place the macOS arm64 build of libmlxsharp.dylib here before packing MLXSharp.Native for distribution. +Place the macOS arm64 build of libmlxsharp.dylib here before packing MLXSharp for distribution. diff --git a/src/MLXSharp/MLXSharp.csproj b/src/MLXSharp/MLXSharp.csproj index 993772e..03e3aed 100644 --- a/src/MLXSharp/MLXSharp.csproj +++ b/src/MLXSharp/MLXSharp.csproj @@ -15,8 +15,11 @@ - $(MSBuildProjectDirectory)..\..\native\build\libmlxsharp.dylib - ..\MLXSharp.Native\runtimes\osx-arm64\native\libmlxsharp.dylib + $(MSBuildThisFileDirectory) + $(MLXSharpProjectDirectory)..\MLXSharp.Native\runtimes + $(MLXSharpProjectDirectory)..\..\native\build\libmlxsharp.dylib + $(MLXSharpNativeRuntimeRoot)\osx-arm64\native\libmlxsharp.dylib + $([System.IO.Path]::GetDirectoryName('$(MLXSharpMacNativeDestination)')) false @@ -28,11 +31,24 @@ Condition="Exists('$(MLXSharpMacNativeBinary)')" /> + + + + + true + runtimes/osx-arm64/native/%(Filename)%(Extension) + PreserveNewest + runtimes\osx-arm64\native\%(Filename)%(Extension) + + +