Skip to content

Commit c60f3f8

Browse files
committed
init
1 parent da8b59c commit c60f3f8

31 files changed

+1851
-1
lines changed

.github/workflows/ci.yml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
8+
jobs:
9+
build-test:
10+
runs-on: macos-latest
11+
steps:
12+
- name: Checkout repository
13+
uses: actions/checkout@v4
14+
with:
15+
submodules: recursive
16+
17+
- name: Setup .NET
18+
uses: actions/setup-dotnet@v4
19+
with:
20+
dotnet-version: 9.0.x
21+
include-prerelease: true
22+
23+
- name: Show .NET info
24+
run: dotnet --info
25+
26+
- name: Restore dependencies
27+
run: dotnet restore MLXSharp.sln
28+
29+
- name: Build
30+
run: dotnet build MLXSharp.sln --configuration Release --no-restore
31+
32+
- name: Prepare artifact folders
33+
run: |
34+
mkdir -p artifacts/test-results
35+
mkdir -p artifacts/packages
36+
mkdir -p artifacts/native
37+
38+
- name: Build native wrapper
39+
run: |
40+
cmake -S native -B native/build -DCMAKE_BUILD_TYPE=Release
41+
cmake --build native/build --target mlxsharp
42+
cp native/build/libmlxsharp.dylib artifacts/native/
43+
44+
- name: Run tests
45+
run: dotnet test MLXSharp.sln --configuration Release --no-build --logger "trx;LogFileName=TestResults.trx" --results-directory artifacts/test-results
46+
47+
- name: Pack library
48+
run: dotnet pack src/MLXSharp/MLXSharp.csproj --configuration Release --no-build --output artifacts/packages
49+
50+
- name: Upload native artifact
51+
uses: actions/upload-artifact@v4
52+
with:
53+
name: native-libs
54+
path: artifacts/native
55+
56+
- name: Upload packages artifact
57+
uses: actions/upload-artifact@v4
58+
with:
59+
name: packages
60+
path: artifacts/packages
61+
62+
- name: Upload test results
63+
if: always()
64+
uses: actions/upload-artifact@v4
65+
with:
66+
name: test-results
67+
path: artifacts/test-results

.gitignore

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,3 +416,23 @@ FodyWeavers.xsd
416416
*.msix
417417
*.msm
418418
*.msp
419+
420+
# MLXSharp native and tooling artifacts
421+
native/build/
422+
native/out/
423+
cmake-build*/
424+
CMakeCache.txt
425+
CMakeFiles/
426+
Testing/
427+
_deps/
428+
compile_commands.json
429+
Makefile
430+
build.ninja
431+
.ninja_deps
432+
.ninja_log
433+
434+
# Miscellaneous host-specific clutter
435+
.DS_Store
436+
Thumbs.db
437+
*.swp
438+
*.swo

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "extern/mlx"]
2+
path = extern/mlx
3+
url = https://github.com/ml-explore/mlx

MLXSharp.sln

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.31903.59
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
7+
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLXSharp", "src\MLXSharp\MLXSharp.csproj", "{69BB544C-9ADD-4561-8B9D-7ACED3B46296}"
9+
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLXSharp.Tests", "src\MLXSharp.Tests\MLXSharp.Tests.csproj", "{8F2FA235-E636-4D11-A55D-450505BB8F19}"
11+
EndProject
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLXSharp.Native", "src\MLXSharp.Native\MLXSharp.Native.csproj", "{F5B45C67-1810-4A04-862F-C33A7DFAE4C2}"
13+
EndProject
14+
Global
15+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
16+
Debug|Any CPU = Debug|Any CPU
17+
Debug|x64 = Debug|x64
18+
Debug|x86 = Debug|x86
19+
Release|Any CPU = Release|Any CPU
20+
Release|x64 = Release|x64
21+
Release|x86 = Release|x86
22+
EndGlobalSection
23+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
24+
{69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25+
{69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Debug|Any CPU.Build.0 = Debug|Any CPU
26+
{69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Debug|x64.ActiveCfg = Debug|Any CPU
27+
{69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Debug|x64.Build.0 = Debug|Any CPU
28+
{69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Debug|x86.ActiveCfg = Debug|Any CPU
29+
{69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Debug|x86.Build.0 = Debug|Any CPU
30+
{69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Release|Any CPU.ActiveCfg = Release|Any CPU
31+
{69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Release|Any CPU.Build.0 = Release|Any CPU
32+
{69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Release|x64.ActiveCfg = Release|Any CPU
33+
{69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Release|x64.Build.0 = Release|Any CPU
34+
{69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Release|x86.ActiveCfg = Release|Any CPU
35+
{69BB544C-9ADD-4561-8B9D-7ACED3B46296}.Release|x86.Build.0 = Release|Any CPU
36+
{8F2FA235-E636-4D11-A55D-450505BB8F19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37+
{8F2FA235-E636-4D11-A55D-450505BB8F19}.Debug|Any CPU.Build.0 = Debug|Any CPU
38+
{8F2FA235-E636-4D11-A55D-450505BB8F19}.Debug|x64.ActiveCfg = Debug|Any CPU
39+
{8F2FA235-E636-4D11-A55D-450505BB8F19}.Debug|x64.Build.0 = Debug|Any CPU
40+
{8F2FA235-E636-4D11-A55D-450505BB8F19}.Debug|x86.ActiveCfg = Debug|Any CPU
41+
{8F2FA235-E636-4D11-A55D-450505BB8F19}.Debug|x86.Build.0 = Debug|Any CPU
42+
{8F2FA235-E636-4D11-A55D-450505BB8F19}.Release|Any CPU.ActiveCfg = Release|Any CPU
43+
{8F2FA235-E636-4D11-A55D-450505BB8F19}.Release|Any CPU.Build.0 = Release|Any CPU
44+
{8F2FA235-E636-4D11-A55D-450505BB8F19}.Release|x64.ActiveCfg = Release|Any CPU
45+
{8F2FA235-E636-4D11-A55D-450505BB8F19}.Release|x64.Build.0 = Release|Any CPU
46+
{8F2FA235-E636-4D11-A55D-450505BB8F19}.Release|x86.ActiveCfg = Release|Any CPU
47+
{8F2FA235-E636-4D11-A55D-450505BB8F19}.Release|x86.Build.0 = Release|Any CPU
48+
{F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49+
{F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
50+
{F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Debug|x64.ActiveCfg = Debug|Any CPU
51+
{F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Debug|x64.Build.0 = Debug|Any CPU
52+
{F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Debug|x86.ActiveCfg = Debug|Any CPU
53+
{F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Debug|x86.Build.0 = Debug|Any CPU
54+
{F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
55+
{F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Release|Any CPU.Build.0 = Release|Any CPU
56+
{F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Release|x64.ActiveCfg = Release|Any CPU
57+
{F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Release|x64.Build.0 = Release|Any CPU
58+
{F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Release|x86.ActiveCfg = Release|Any CPU
59+
{F5B45C67-1810-4A04-862F-C33A7DFAE4C2}.Release|x86.Build.0 = Release|Any CPU
60+
EndGlobalSection
61+
GlobalSection(SolutionProperties) = preSolution
62+
HideSolutionNode = FALSE
63+
EndGlobalSection
64+
GlobalSection(NestedProjects) = preSolution
65+
{69BB544C-9ADD-4561-8B9D-7ACED3B46296} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
66+
{8F2FA235-E636-4D11-A55D-450505BB8F19} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
67+
{F5B45C67-1810-4A04-862F-C33A7DFAE4C2} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
68+
EndGlobalSection
69+
EndGlobal

README.md

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,98 @@
11
# MLXSharp
2-
MLX Binding for C#
2+
3+
MLXSharp offers .NET bindings and tooling around [Apple MLX](https://github.com/ml-explore/mlx) that plug directly into the [`Microsoft.Extensions.AI`](https://learn.microsoft.com/dotnet/ai/microsoft-extensions-ai) ecosystem.
4+
The design mirrors the packaging approach from projects such as [LLamaSharp](https://github.com/SciSharp/LLamaSharp): managed clients sit on top of a native wrapper that can be distributed through NuGet alongside prebuilt binaries.
5+
6+
## Features
7+
8+
- `IChatClient`, `IEmbeddingGenerator<string, Embedding<float)>`, and image generation helpers that adhere to the `Microsoft.Extensions.AI` abstractions.
9+
- Builder-based backend configuration with a deterministic managed implementation for tests and a P/Invoke powered native backend.
10+
- Native library resolver that probes application directories, `MLXSHARP_LIBRARY`, or packaged runtimes and loads `libmlxsharp` on demand.
11+
- `MLXSharp.Native` packaging project that ships stub binaries for CI (Linux x64 today) and a placeholder `osx-arm64` folder for the production MLX wrapper.
12+
- Dependency injection extensions (`AddMlx`) and Semantic Kernel integration (`AddMlxChatCompletion`).
13+
- Integration test suite that exercises chat, embedding, image, and Semantic Kernel flows against both managed and native backends.
14+
15+
## Repository Layout
16+
17+
```
18+
├── extern/mlx # Git submodule with the official MLX sources
19+
├── native/ # Native wrapper scaffold (CMake project)
20+
├── src/MLXSharp/ # Managed library with Microsoft.Extensions.AI adapters
21+
├── src/MLXSharp.Native/ # NuGet-ready container for native binaries
22+
└── src/MLXSharp.Tests/ # Integration tests covering DI and Semantic Kernel
23+
```
24+
25+
## Prerequisites
26+
27+
The solution targets .NET 9 / C# 13 previews. Install the SDK via the official installer script:
28+
29+
```bash
30+
curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 9.0.305
31+
export PATH="$PATH:$HOME/.dotnet"
32+
dotnet --version
33+
```
34+
35+
## Getting Started
36+
37+
Register MLX services through dependency injection:
38+
39+
```csharp
40+
var services = new ServiceCollection();
41+
services.AddMlx(builder =>
42+
{
43+
builder.Configure(options =>
44+
{
45+
options.ChatModelId = "mlx-chat";
46+
options.EmbeddingModelId = "mlx-embedding";
47+
});
48+
49+
// Managed fallback backend (default) that keeps tests deterministic.
50+
builder.UseManagedBackend(new MlxManagedBackend());
51+
52+
// Or switch to the native backend once libmlxsharp is available.
53+
// builder.UseNativeBackend();
54+
});
55+
```
56+
57+
Semantic Kernel integration uses the same builder experience:
58+
59+
```csharp
60+
var kernelBuilder = Kernel.CreateBuilder();
61+
kernelBuilder.AddMlxChatCompletion(b => b.UseManagedBackend(new MlxManagedBackend()));
62+
var kernel = kernelBuilder.Build();
63+
64+
var chat = kernel.Services.GetRequiredService<IChatCompletionService>();
65+
var history = new ChatHistory();
66+
history.AddUserMessage("Summarise MLX in one sentence");
67+
var response = await chat.GetChatMessageContentsAsync(history, new PromptExecutionSettings(), kernel, CancellationToken.None);
68+
```
69+
70+
## Native Runtime Packaging
71+
72+
- `src/MLXSharp.Native` mimics NuGet runtime assets with `runtimes/<rid>/native/libmlxsharp.*` folders.
73+
The Linux x64 directory contains a stub library compiled from `native/src/mlxsharp.cpp` so tests can load it.
74+
- Drop the real macOS build (`libmlxsharp.dylib`) into `runtimes/osx-arm64/native/` before packing a release build.
75+
- At runtime `MlxNativeLibrary` probes the explicit `MlxClientOptions.LibraryPath`, the `MLXSHARP_LIBRARY` environment variable, and packaged runtime folders before falling back to system search paths.
76+
77+
To build the native wrapper locally (mirroring the LLamaSharp workflow):
78+
79+
```bash
80+
git submodule update --init --recursive
81+
cmake -S native -B native/build -DCMAKE_BUILD_TYPE=Release
82+
cmake --build native/build
83+
export MLXSHARP_LIBRARY=$(pwd)/native/build/libmlxsharp.dylib
84+
```
85+
86+
The CMake project vendors the official [mlx](https://github.com/ml-explore/mlx) sources as a submodule and
87+
builds them alongside `libmlxsharp`. On non-macOS hosts the configuration automatically disables the Metal backend;
88+
pass `-DMLX_BUILD_METAL=ON` if you are compiling on Apple silicon and want GPU support. Additional MLX switches can be
89+
forwarded on the command line, for example `-DMLX_BUILD_BLAS_FROM_SOURCE=ON` when targeting systems without an OpenBLAS
90+
installation.
91+
92+
## Running Tests
93+
94+
```bash
95+
dotnet test
96+
```
97+
98+
The suite validates the managed backend, Semantic Kernel extensions, and the native loader by exercising the packaged stub library.

extern/mlx

Submodule mlx added at e76a8dd

native/CMakeLists.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
cmake_minimum_required(VERSION 3.24)
2+
project(mlxsharp C CXX)
3+
4+
set(CMAKE_C_STANDARD 17)
5+
set(CMAKE_CXX_STANDARD 17)
6+
7+
set(MLX_BUILD_TESTS OFF CACHE BOOL "" FORCE)
8+
set(MLX_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
9+
set(MLX_BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE)
10+
set(MLX_BUILD_PYTHON_BINDINGS OFF CACHE BOOL "" FORCE)
11+
12+
if(NOT APPLE)
13+
set(MLX_BUILD_METAL OFF CACHE BOOL "" FORCE)
14+
endif()
15+
16+
add_subdirectory(${CMAKE_SOURCE_DIR}/../extern/mlx ${CMAKE_BINARY_DIR}/extern/mlx EXCLUDE_FROM_ALL)
17+
18+
add_library(mlxsharp SHARED src/mlxsharp.cpp)
19+
20+
target_link_libraries(mlxsharp PRIVATE mlx)
21+
22+
target_include_directories(mlxsharp PRIVATE ${CMAKE_SOURCE_DIR}/../extern/mlx/mlx)
23+
24+
target_compile_definitions(mlxsharp PRIVATE MLXSHARP_EXPORTS)
25+
26+
install(TARGETS mlxsharp DESTINATION lib)

0 commit comments

Comments
 (0)