ElBruno.LocalLLMs.BitNet
0.20.1
dotnet add package ElBruno.LocalLLMs.BitNet --version 0.20.1
NuGet\Install-Package ElBruno.LocalLLMs.BitNet -Version 0.20.1
<PackageReference Include="ElBruno.LocalLLMs.BitNet" Version="0.20.1" />
<PackageVersion Include="ElBruno.LocalLLMs.BitNet" Version="0.20.1" />
<PackageReference Include="ElBruno.LocalLLMs.BitNet" />
paket add ElBruno.LocalLLMs.BitNet --version 0.20.1
#r "nuget: ElBruno.LocalLLMs.BitNet, 0.20.1"
#:package ElBruno.LocalLLMs.BitNet@0.20.1
#addin nuget:?package=ElBruno.LocalLLMs.BitNet&version=0.20.1
#tool nuget:?package=ElBruno.LocalLLMs.BitNet&version=0.20.1
ElBruno.LocalLLMs
Run local LLMs in .NET through IChatClient π§
Run local LLMs in .NET through IChatClient β the same interface you'd use for Azure OpenAI, Ollama, or any other provider. Powered by ONNX Runtime GenAI and BitNet.
What's New
- π¦ Transitive native runtime fix (
v0.20.1) β AddedbuildTransitivepackaging soonnxruntime-genai.dllis copied for downstream consumers that referenceElBruno.LocalLLMstransitively (Issue #24). - π€ Qwen3 & MagenticBrain support (
v0.20.0) β NewChatTemplateFormat.Qwen3formatter andKnownModels.MagenticBrain/KnownModels.Qwen3_14BInstructfor agentic multi-agent orchestration loops. - ποΈ Fara 1.5-9B vision-language model (
v0.20.0) β Run Microsoft's Fara VLM locally via the newLocalVisionChatClient,IVisionGenerationModel, andVisionChatOptionswith image paths. - π ElBruno.MagenticUI reference app β Full Blazor Server multi-agent app (FileSurfer, WebFetcher, Coder, UserProxy) powered by this library. Drop-in .NET port of microsoft/magentic-ui with human-in-the-loop support.
- π‘ OpenTelemetry diagnostics (
v0.19.0) β Generation lifecycle activities and metrics (gen_ai.client.*) viaActivitySource+Meterboth namedElBruno.LocalLLMs. - β
Gemma 4 family active β
E2B,E4B,12B Unified,26B-A4B,31Ball supported via conversion workflows. - β¬οΈ ONNX Runtime GenAI
0.14.1β upgraded across library, tests, samples, and benchmarks.
Features
- π
IChatClientimplementation β seamless integration with Microsoft.Extensions.AI - π¦ Automatic model download β models are fetched from HuggingFace on first use
- π Zero friction β works out of the box with sensible defaults (Phi-3.5 mini)
- π₯οΈ Multi-hardware β CPU, CUDA, and DirectML execution providers
- π DI-friendly β register with
AddLocalLLMs()orAddBitNetChatClient()in ASP.NET Core - π Streaming β token-by-token streaming via
GetStreamingResponseAsync - π Multi-model β switch between Phi-3.5, Phi-4, Qwen2.5, Qwen3, Llama 3.2, MagenticBrain, and more
- ποΈ Vision-language models β run Fara 1.5-9B image+text models via
LocalVisionChatClient - π€ Agentic models β Qwen3 / MagenticBrain support for multi-agent orchestration loops
- π― Fine-tuned models β pre-trained Qwen2.5 variants for tool calling and RAG (guide)
- β‘ BitNet support β run 1.58-bit ternary models via bitnet.cpp with extreme efficiency (guide)
- π OpenTelemetry diagnostics β lifecycle activities and metrics for queued, first-token, completion, cancellation, and failure (guide)
Packages
Installation
dotnet add package ElBruno.LocalLLMs
For CPU scenarios, no extra package is required β the transitive buildTransitive shim copies onnxruntime-genai.dll automatically on Windows.
Add a runtime package only when you want a specific GPU provider:
# π’ NVIDIA GPU (CUDA):
dotnet add package Microsoft.ML.OnnxRuntimeGenAI.Cuda
# π΅ Any Windows GPU β AMD, Intel, NVIDIA (DirectML):
dotnet add package Microsoft.ML.OnnxRuntimeGenAI.DirectML
β οΈ Add at most one GPU runtime package. Do not reference both
Microsoft.ML.OnnxRuntimeGenAI.CudaandMicrosoft.ML.OnnxRuntimeGenAI.DirectMLsimultaneously.If you use a GPU runtime package and want to disable the transitive CPU copy shim, set:
<ElBrunoLocalLLMsDisableCpuNativeCopy>true</ElBrunoLocalLLMsDisableCpuNativeCopy>in your application.csproj.
π The library defaults to
ExecutionProvider.Autoβ it tries GPU first and falls back to CPU automatically. No code changes needed.
Quick Start
using ElBruno.LocalLLMs;
using Microsoft.Extensions.AI;
// Create a local chat client (downloads Phi-3.5 mini on first run)
using var client = await LocalChatClient.CreateAsync();
var response = await client.GetResponseAsync([
new(ChatRole.User, "What is the capital of France?")
]);
Console.WriteLine(response.Text);
First Run
The first time you create a LocalChatClient, the model is downloaded from HuggingFace to your local cache directory (~2-4 GB). This typically takes 30-60 seconds depending on your internet connection.
Track download progress:
using var client = await LocalChatClient.CreateAsync(
new LocalLLMsOptions { Model = KnownModels.Phi35MiniInstruct },
progress: new Progress<ModelDownloadProgress>(p =>
{
var percent = (p.BytesDownloaded * 100) / p.TotalBytes;
Console.WriteLine($"{p.FileName}: {percent:F1}%");
})
);
Subsequent runs load instantly from cache (%LOCALAPPDATA%/ElBruno/LocalLLMs/models).
Skip auto-download if using a pre-downloaded model:
var options = new LocalLLMsOptions
{
Model = KnownModels.Phi35MiniInstruct,
ModelPath = "/path/to/local/model",
EnsureModelDownloaded = false
};
using var client = await LocalChatClient.CreateAsync(options);
Streaming
using ElBruno.LocalLLMs;
using Microsoft.Extensions.AI;
using var client = await LocalChatClient.CreateAsync(new LocalLLMsOptions
{
Model = KnownModels.Phi35MiniInstruct
});
await foreach (var update in client.GetStreamingResponseAsync([
new(ChatRole.System, "You are a helpful assistant."),
new(ChatRole.User, "Explain quantum computing in simple terms.")
]))
{
Console.Write(update.Text);
}
GPU Acceleration
By default, ExecutionProvider.Auto tries GPU first (CUDA β DirectML) and falls back to CPU automatically:
// Use explicit GPU provider (fails if CUDA not installed; use Auto to fallback to CPU)
var options = new LocalLLMsOptions
{
ExecutionProvider = ExecutionProvider.Cuda
};
// Multi-GPU systems: select device ID
var options2 = new LocalLLMsOptions
{
ExecutionProvider = ExecutionProvider.Cuda,
GpuDeviceId = 1 // Use second GPU
};
Auto fallback behavior:
- CUDA available β uses NVIDIA GPU
- CUDA unavailable, DirectML available β uses AMD/Intel Arc GPU
- GPU unavailable β falls back to CPU (no errors, just slower)
See Troubleshooting: GPU Setup for debugging GPU issues.
Model Metadata
Inspect model capabilities at runtime β context window size, model name, and vocabulary:
using var client = await LocalChatClient.CreateAsync();
var metadata = client.ModelInfo;
Console.WriteLine($"Model: {metadata?.ModelName}");
Console.WriteLine($"Context window: {metadata?.MaxSequenceLength}");
Console.WriteLine($"Vocab size: {metadata?.VocabSize}");
This is useful for prompt-length validation, adaptive chunking, and model selection logic.
Dependency Injection
builder.Services.AddLocalLLMs(options =>
{
options.Model = KnownModels.Phi35MiniInstruct;
options.ExecutionProvider = ExecutionProvider.DirectML;
});
// Inject IChatClient anywhere
public class MyService(IChatClient chatClient) { ... }
Error Handling
The library provides structured exception types for graceful error handling:
using ElBruno.LocalLLMs;
using Microsoft.Extensions.AI;
try
{
using var client = await LocalChatClient.CreateAsync();
var response = await client.GetResponseAsync([
new(ChatRole.User, "Your question here")
]);
}
catch (ExecutionProviderException ex)
{
// GPU/provider-specific error (no CUDA, DirectML not available, etc.)
Console.WriteLine($"Provider error: {ex.Message}");
}
catch (ModelCapacityExceededException ex)
{
// Prompt/response too long for model's context window
Console.WriteLine($"Capacity error: {ex.Message}");
// Solution: use a larger model or truncate the prompt
}
catch (InvalidOperationException ex)
{
// General operation error (model not found, download failed, etc.)
Console.WriteLine($"Operation error: {ex.Message}");
}
Observability
LocalChatClient emits generation lifecycle diagnostics through ActivitySource and Meter, both named ElBruno.LocalLLMs.
using ElBruno.LocalLLMs.Diagnostics;
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing.AddSource(LocalLLMsInstrumentation.ActivitySourceName))
.WithMetrics(metrics => metrics.AddMeter(LocalLLMsInstrumentation.MeterName));
By default, telemetry excludes prompt and completion text. Opt in only when you want content attached:
var options = new LocalLLMsOptions
{
CaptureTelemetryContent = true
};
See docs/observability.md for the lifecycle event contract, metric names, and Aspire wiring notes, and docs/cancellation.md for voice barge-in cancellation behavior.
Troubleshooting
GPU not working? Use ExecutionProvider.Cpu explicitly. See GPU Setup Validation.
Out of memory? Try a smaller model:
var options = new LocalLLMsOptions
{
Model = KnownModels.Qwen25_05BInstruct // 0.5B instead of 3.8B
};
Model download fails?
- Check your internet connection
- For private HuggingFace models, set the
HF_TOKENenvironment variable
For detailed troubleshooting, see docs/troubleshooting-guide.md.
Supported Models
| Tier | Model | Parameters | ONNX | ID |
|---|---|---|---|---|
| βͺ Tiny | TinyLlama-1.1B-Chat | 1.1B | β Native | tinyllama-1.1b-chat |
| βͺ Tiny | SmolLM2-1.7B-Instruct | 1.7B | β Native | smollm2-1.7b-instruct |
| βͺ Tiny | Qwen2.5-0.5B-Instruct | 0.5B | β Native | qwen2.5-0.5b-instruct |
| βͺ Tiny | Qwen2.5-1.5B-Instruct | 1.5B | β Native | qwen2.5-1.5b-instruct |
| βͺ Tiny | Gemma-2B-IT | 2B | β Native | gemma-2b-it |
| βͺ Tiny | Gemma-4-E2B-IT | 5.1B (2B active) | π Convert | gemma-4-e2b-it |
| βͺ Tiny | StableLM-2-1.6B-Chat | 1.6B | π Convert | stablelm-2-1.6b-chat |
| π’ Small | Phi-3.5 mini instruct | 3.8B | β Native | phi-3.5-mini-instruct |
| π’ Small | Qwen2.5-3B-Instruct | 3B | β Native | qwen2.5-3b-instruct |
| π’ Small | Llama-3.2-3B-Instruct | 3B | β Native | llama-3.2-3b-instruct |
| π’ Small | Gemma-2-2B-IT | 2B | β Native | gemma-2-2b-it |
| π’ Small | Gemma-4-E4B-IT | 8B (4B active) | π Convert | gemma-4-e4b-it |
| π‘ Medium | Qwen2.5-7B-Instruct | 7B | β Native | qwen2.5-7b-instruct |
| π‘ Medium | Qwen2.5-Coder-7B-Instruct | 7B | π Convert | qwen2.5-coder-7b-instruct |
| π‘ Medium | Llama-3.1-8B-Instruct | 8B | β Native | llama-3.1-8b-instruct |
| π‘ Medium | Mistral-7B-Instruct-v0.3 | 7B | β Native | mistral-7b-instruct-v0.3 |
| π‘ Medium | Gemma-2-9B-IT | 9B | β Native | gemma-2-9b-it |
| π‘ Medium | Gemma-4-12B-IT | 12B | π Convert | gemma-4-12b-it |
| π‘ Medium | Phi-4 | 14B | β Native | phi-4 |
| π‘ Medium | DeepSeek-R1-Distill-Qwen-14B | 14B | β Native | deepseek-r1-distill-qwen-14b |
| π‘ Medium | Mistral-Small-24B-Instruct | 24B | β Native | mistral-small-24b-instruct |
| π΄ Large | Qwen2.5-14B-Instruct | 14B | β Native | qwen2.5-14b-instruct |
| π΄ Large | Qwen2.5-32B-Instruct | 32B | β Native | qwen2.5-32b-instruct |
| π΄ Large | Llama-3.3-70B-Instruct | 70B | β ONNX | llama-3.3-70b-instruct |
| π΄ Large | Mixtral-8x7B-Instruct-v0.1 | 8x7B | π Convert | mixtral-8x7b-instruct-v0.1 |
| π΄ Large | DeepSeek-R1-Distill-Llama-70B | 70B | π Convert | deepseek-r1-distill-llama-70b |
| π΄ Large | Command-R (35B) | 35B | π Convert | command-r-35b |
| π΄ Large | Gemma-4-26B-A4B-IT | 25.2B (3.8B active) | π Convert | gemma-4-26b-a4b-it |
| π΄ Large | Gemma-4-31B-IT | 30.7B | π Convert | gemma-4-31b-it |
| π£ Next-Gen | Qwen3-8B | 8B | π Convert | qwen3-8b |
| π£ Next-Gen | Qwen3-14B-Instruct | 14.77B | β ONNX | qwen3-14b-instruct |
| π£ Next-Gen | Qwen3-32B | 32B | π Convert | qwen3-32b |
| π£ Next-Gen | Gemma-3-12B-IT | 12B | π Convert | gemma-3-12b-it |
| π£ Next-Gen | Llama-4-Scout | ~17B (MoE) | π Convert | llama-4-scout |
| π£ Next-Gen | Llama-4-Maverick | ~17B (MoE) | π Convert | llama-4-maverick |
| π£ Next-Gen | DeepSeek-V3 | 671B (MoE) | π Convert | deepseek-v3 |
| π€ Agentic | MagenticBrain | ~14.77B | β ONNXΒΉ | magentic-brain |
| ποΈ VLM | Fara 1.5-9B | ~9.4B | π ConvertΒ² | fara-1.5-9b |
π Convert = Use the conversion scripts in
scripts/to export ONNX locally before running the model.ΒΉ MagenticBrain ONNX: No official ONNX yet. Use
onnx-community/Qwen3-14B-ONNXas drop-in until Microsoft publishes a native conversion.Β² Fara 1.5-9B ONNX: Convert via ORT-GenAI model builder (
--model_type qwen_vl). UseLocalVisionChatClient(notLocalChatClient). See ONNX Conversion β Fara.
Fine-Tuned Models
Pre-trained variants optimized for specific tasks. A fine-tuned 0.5B model often matches or exceeds a base 1.5B on its specialized task.
| Model | Size | Task | HuggingFace ID |
|---|---|---|---|
| Qwen2.5-0.5B-ToolCalling | ~1 GB | Tool/function calling | elbruno/Qwen2.5-0.5B-LocalLLMs-ToolCalling |
| Qwen2.5-0.5B-RAG | ~1 GB | RAG with citations | elbruno/Qwen2.5-0.5B-LocalLLMs-RAG |
| Qwen2.5-0.5B-Instruct | ~1 GB | General-purpose | elbruno/Qwen2.5-0.5B-LocalLLMs-Instruct |
See the Supported Models Guide for detailed model cards, performance benchmarks, and selection guidance.
Samples
| Sample | Description |
|---|---|
| HelloChat | Minimal console chat |
| StreamingChat | Token-by-token streaming |
| MultiModelChat | Switch models at runtime |
| DependencyInjection | ASP.NET Core DI registration |
| ToolCallingAgent | Function calling and tool use |
| FineTunedToolCalling | Fine-tuned model for improved tool calling |
| RagChatbot | RAG pipeline with document retrieval |
| ZeroCloudRag | Zero-cloud RAG pipeline with real local embeddings and LLM inference |
| BitNetChat | BitNet 1.58-bit model chat completion |
| BitNetPerformance | Performance benchmark: BitNet vs ONNX models |
| MagenticBrainAgent | Multi-agent orchestration loop using Qwen3/MagenticBrain |
| FaraVisionAgent | Vision-language model (Fara 1.5-9B) image+text inference |
| MagenticUIServer | ASP.NET Core + SignalR multi-agent server (FileSurfer, WebFetcher, Coder) |
| ConsoleAppDemo | Interactive console application |
π Reference App: ElBruno.MagenticUI β full Blazor Server port of microsoft/magentic-ui running locally with this library.
Requirements
- .NET 8.0 or .NET 10.0
- CPU (default), NVIDIA GPU (CUDA), or Windows GPU (DirectML)
- ~2-8 GB disk space per model (depending on size and quantization)
Building from Source
git clone https://github.com/elbruno/ElBruno.LocalLLMs.git
cd ElBruno.LocalLLMs
dotnet restore ElBruno.LocalLLMs.slnx
dotnet build ElBruno.LocalLLMs.slnx
dotnet test ElBruno.LocalLLMs.slnx --framework net8.0
Documentation
- Getting Started β installation, first steps, configuration
- Supported Models β full model reference with tiers, specs, decision tree
- BitNet Guide β setup and usage of 1.58-bit BitNet models
- Cancellation Guide β streaming cancellation semantics and voice barge-in usage
- Observability β OpenTelemetry, lifecycle events, metrics, Aspire wiring
- Architecture β design decisions and internal structure
- Samples Guide β walkthrough of each sample application
- Benchmarks β how to run and interpret performance benchmarks
- Fine-Tuning Guide β using and training fine-tuned models
- ONNX Conversion β converting HuggingFace models to ONNX format
- ONNX Conversion β Fara VLM β converting Fara 1.5-9B vision-language model
- Publishing β NuGet package publishing with OIDC
- Contributing β how to contribute
- Changelog β version history
π€ Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
π License
This project is licensed under the MIT License β see the LICENSE file for details.
π About the Author
Hi! I'm ElBruno π§‘, a passionate developer and content creator exploring AI, .NET, and modern development practices.
Made with β€οΈ by ElBruno
If you like this project, consider following my work across platforms:
- π» Podcast: No Tienen Nombre β Spanish-language episodes on AI, development, and tech culture
- π» Blog: ElBruno.com β Deep dives on embeddings, RAG, .NET, and local AI
- πΊ YouTube: youtube.com/elbruno β Demos, tutorials, and live coding
- π LinkedIn: @elbruno β Professional updates and insights
- π Twitter: @elbruno β Quick tips, releases, and tech news
π Acknowledgments
- ONNX Runtime GenAI β inference engine
- BitNet / bitnet.cpp β 1.58-bit ternary model inference
- Microsoft.Extensions.AI β IChatClient interface
- Hugging Face β model hosting and community
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- ElBruno.LocalLLMs (>= 0.20.1)
- Microsoft.Extensions.AI.Abstractions (>= 10.4.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.0)
-
net8.0
- ElBruno.LocalLLMs (>= 0.20.1)
- Microsoft.Extensions.AI.Abstractions (>= 10.4.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.