Starlings.Models.E5 0.3.0

dotnet add package Starlings.Models.E5 --version 0.3.0
                    
NuGet\Install-Package Starlings.Models.E5 -Version 0.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Starlings.Models.E5" Version="0.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Starlings.Models.E5" Version="0.3.0" />
                    
Directory.Packages.props
<PackageReference Include="Starlings.Models.E5" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Starlings.Models.E5 --version 0.3.0
                    
#r "nuget: Starlings.Models.E5, 0.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Starlings.Models.E5@0.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Starlings.Models.E5&version=0.3.0
                    
Install as a Cake Addin
#tool nuget:?package=Starlings.Models.E5&version=0.3.0
                    
Install as a Cake Tool

Starlings.Models.E5

Sentence embeddings from intfloat/multilingual-e5-small at revision 614241f622f53c4eeff9890bdc4f31cfecc418b3, on ONNX Runtime 1.30.0, in-process and offline after an explicit download. The package is versioned 0.x, so the API may still change between minor versions; maturity is per profile (below).

  • Text goes through the owned tokenizer Starlings.Tokenizers (exact against huggingface/tokenizers on the published fixtures). No Microsoft.ML.Tokenizers and no native tokenizer library.
  • Batching with right padding, mean pooling over the attention mask, L2 normalization: 384-dimensional unit vectors.
  • The package ships the model's asset lock (repository, commit, file sizes and SHA-256), not the 487 MB of model files. Acquisition is an explicit call; inference never downloads.

Maturity

Maturity is per profile, generated from the registry manifests by model-pipeline catalog (do not edit this table by hand). Gates, evidence and capabilities per profile: the model catalog.

Profile Maturity Platforms
multilingual-e5-small/onnx-fp32-cpu stable linux-x64 cpu: supported; windows-x64 cpu: experimental
multilingual-e5-small/onnx-fp32-cuda stable linux-x64 cuda: supported; windows-x64 cuda: untested

Install

dotnet add package Starlings.Models.E5
dotnet add package Microsoft.ML.OnnxRuntime --version 1.30.0          # CPU natives
# or, for CUDA on Linux x64: Microsoft.ML.OnnxRuntime.Gpu.Linux 1.30.0 (reference exactly one)

The library references only Microsoft.ML.OnnxRuntime.Managed 1.30.0; the application chooses the native runtime.

Use

using Starlings.Models.E5;
using Starlings.Runtime.Onnx;

await E5ModelAssets.DownloadAsync();                 // once, explicit, needs the network; verifies SHA-256
E5ModelFiles files = E5ModelAssets.OpenLocal();      // offline, verified, never downloads

using var encoder = E5Encoder.Create(files.ModelPath, new E5EncoderOptions
{
    Session = new OnnxSessionOptions { ExpectedModelSha256 = E5ModelAssets.ModelSha256 },
});
var embedder = new E5TextEmbedder(E5Tokenizer.FromFile(files.TokenizerPath, E5ModelAssets.TokenizerSha256), encoder);

float[] query = embedder.Embed(E5TextEmbedder.QueryPrefix + "how much protein should a female eat");
float[][] passages = embedder.EmbedBatch([E5TextEmbedder.PassagePrefix + "As a general guideline, ..."]);

e5 expects "query: " or "passage: " at the start of every text; nothing is added for you. Vectors are normalized, so cosine similarity is the dot product.

Batching options

EmbedBatch splits a batch into ONNX Runtime calls, each right-padded to its longest row. E5EncoderOptions controls the split:

Option Default Effect
MaxBatchSize 32 Rows per call at most. Chunks are consecutive rows in input order.
MaxBatchTokens null (no budget) Padded cells (rows x longest row) per call at most. A chunk closes before it would exceed the budget; a row longer than the budget runs alone. Bounds memory and attention work for long inputs. Experimental (batch.token_budget).
SortByLength false Chunks rows in ascending token length (ties in input order) so each call pads less, and writes each result back to its input position. Experimental (batch.sort_by_length).

The defaults are the qualified behavior and give the same outputs as before these options existed, bit for bit. With SortByLength, which rows share a call depends on the whole batch, so a row's embedding can differ from the default by up to the profile's padded-batch-vs-single bound (padded_batch_vs_single_embedding_max_abs; on ONNX Runtime CPU the tests saw no difference). If a call is cancelled, the Span<float> overload may already hold the rows of the chunks that completed: a prefix of the rows by default, the shortest rows with SortByLength.

Status

Profile multilingual-e5-small/onnx-fp32-cpu (Linux x64 CPU) is Stable since 2026-10-15: it passed every qualification gate, G0 to G7, and the weekly qualification workflow reruns G1 to G3 on the release sets and a one-hour soak, green. The hosted soak that failed on 2026-10-15 (about 50 MB/h) did so because of glibc retention after unloading and transparent huge pages set to always on the runner image, not a leak (evidence). On 2026-10-16 one hosted soak in six failed again (58 MiB of unloaded growth against 32): requests from rotating thread-pool threads left freed memory resident in more and more glibc heaps. With dedicated run threads on CPU (below), those heaps stay at 14 to 62 MiB after unloading (evidence). Profile multilingual-e5-small/onnx-fp32-cuda (Linux x64, NVIDIA GeForce RTX 3090) is Stable since 2026-10-16 (Stable contract: CUDA): every gate passed, and the per-release GPU regression run was green for it. GPU regression runs for every release and on demand, on an RTX 3090 only (the qualification workflow's cuda (runpod) job); CPU regression runs weekly. Gate evidence: the catalog. Model license: MIT, stated only in the model card.

Stable contract

This is the contract of profile multilingual-e5-small/onnx-fp32-cpu. Its Stable label (since 2026-10-15) commits to it under the support policy. The statuses are the registry manifest's and E5Capabilities.Profiles, which a test keeps equal key for key.

Item Contract
Profile multilingual-e5-small/onnx-fp32-cpu (registry manifest model-pipeline/registry/multilingual-e5-small/onnx-fp32-cpu/manifest.yaml).
Model intfloat/multilingual-e5-small at 614241f622f53c4eeff9890bdc4f31cfecc418b3; onnx/model.onnx and tokenizer.json verified by SHA-256 (E5ModelAssets.Lock).
Tokenizer Starlings.Tokenizers 0.3.0, profile xlmr-unigram-e5 (pinned tokenizer.json, not SentencePiece or transformers' rebuilt tokenizer).
Runtime ONNX Runtime 1.30.0 (Microsoft.ML.OnnxRuntime.Managed; the application adds Microsoft.ML.OnnxRuntime 1.30.0 CPU natives), fp32, .NET 10.
Supported platform Linux x64, CPU. Gates G1 to G5 ran on an Intel Core i7-5820K (Haswell-E, 6 cores and 12 threads, AVX2 and FMA, no AVX-512), .NET 10.0.12. The weekly qualification reruns G1 to G3 on the release sets and a one-hour soak on GitHub-hosted ubuntu-latest x64 (4 logical CPUs; transparent huge pages madvise). No MALLOC_* environment setting is required.
Memory on Linux The runtime calls malloc_trim(0) when a session is released, on by default. CPU sessions run on the runtime's dedicated run threads unless OnnxSessionOptions.DedicatedRunThreads is set to false; outputs are the same. See Memory on Linux.
Input Text, or token ids (EncoderInput). The caller adds "query: " or "passage: "; nothing is inserted.
Output 384-dimensional float32 vectors: mean pooling over the attention mask, then L2 normalization. Batches are right-padded; batch rows equal single calls within the protected tolerances.
Numerical bound The protected tolerances of the profile (embedding max abs 1e-5 against the PyTorch reference). Release set: 1.46e-7.
Task quality MIRACL dev, 8 languages: nDCG@10 at least 0.792 and MRR@10 at least 0.765 (budget); measured 0.7978 and 0.7717.
Supported Embedding from text and from token ids; batch; cancellation (at ONNX Runtime node boundaries on CPU, after the current run on CUDA); concurrent calls; explicit acquisition (download, verify, offline reuse); Native AOT for the typed API and the Extensions.AI generator; Starlings.Models.E5.Extensions.AI (IEmbeddingGenerator); Starlings.Models.E5.MLNet (pipeline composition, row-wise and look-ahead batching).
Limits 512 tokens including <s> and </s>. E5Tokenizer truncates on the right to 512 (the profile's truncate-right, as the reference); E5Encoder rejects id sequences longer than MaxSequenceLength and never truncates them. The model files (487 MB) are acquired explicitly; inference never downloads.
Outside the Stable contract Windows x64 CPU: experimental (CI builds, runs the weight-free tests and the clean-install drill without the model; no model run). CUDA is the separate profile onnx-fp32-cuda with its own contract (Stable contract: CUDA). macOS, ARM64, Windows CUDA: unsupported (no evidence).
Unsupported streaming, pair input, non-zero token_type_ids, encoder-side truncation, prefix insertion, TF32, fp16, quantized graphs, TensorRT, persistence (ML.NET save), training, ML.NET cancellation, the ML.NET adapter under Native AOT, Extensions.AI Dimensions truncation.
Known issues On hosts with transparent huge pages set to always, resident memory can grow over time with no leak behind it (Memory on Linux). The pinned tokenizer.json emits a lone ▁ token before special-token literals and for trailing whitespace, where SentencePiece and transformers 5.18 emit none; the profile follows tokenizer.json. It also leaves decomposed Hangul jamo uncomposed, while SentencePiece composes them. Both are recorded, not fixed.

Stable contract: CUDA

This is the contract of profile multilingual-e5-small/onnx-fp32-cuda (Stable since 2026-10-16). It is the CPU contract above, with these differences. The statuses are the registry manifest's and E5Capabilities.Profiles, which a test keeps equal key for key.

Item Contract
Profile multilingual-e5-small/onnx-fp32-cuda (registry manifest model-pipeline/registry/multilingual-e5-small/onnx-fp32-cuda/manifest.yaml). The same model and tokenizer as the CPU profile.
Runtime ONNX Runtime 1.30.0 with the CUDA execution provider (the application adds Microsoft.ML.OnnxRuntime.Gpu.Linux 1.30.0 instead of the CPU natives), use_tf32=0, fp32. The CUDA 13 runtime and cuDNN 9 must be on the loader path; the package does not ship them.
Supported platform Linux x64 with an NVIDIA GeForce RTX 3090 (Ampere), driver 580 or later. G1 to G3 on the release sets, the binding G4 one-hour soak and G5 ran on RTX 3090s. Regression: the per-release GPU run (the qualification workflow's cuda (runpod) job, for every ai-v* tag and on demand) reruns the CUDA tests (the adapters over CUDA included), G1 to G3 on the release sets and a soak smoke test, on an RTX 3090 only; the job refuses any other GPU. Other GPUs, including the RTX 4090 (Ada), are untested and outside the contract.
Memory on Linux No MALLOC_* setting is required; heap trim on release as on CPU.
Cancellation A CUDA run is never terminated mid-graph: it completes, then the call throws OperationCanceledException (#61). Latency is one chunk, about 125 ms for 32 x 512 tokens.
Numerical bound The CUDA profile's protected tolerances (embedding max abs 1e-5 against the PyTorch reference). Release set: 1.21e-7; C# against Python ONNX Runtime CUDA 8e-8.
Task quality MIRACL: nDCG@10 0.7978, MRR@10 0.7717 (budget 0.792 and 0.765).
Performance G5 on an RTX 3090: warm latency 0.915 to 1.028 of the Python ONNX Runtime CUDA run (budget 1.05).
Supported Embedding from text and from token ids; batch; cancellation; concurrent calls; explicit acquisition; Starlings.Models.E5.Extensions.AI and Starlings.Models.E5.MLNet over a CUDA session.
Outside the Stable contract Native AOT with the CUDA natives: experimental (one local run, tools/aot-check-ai.sh --cuda, not in the per-release run). Windows x64 CUDA: untested.

Evidence: binding CUDA G4 and G5 round, per-release GPU regression, run 38857860186, release-set gates, CUDA cancellation.

Memory on Linux

  • Heap trim on release (on by default). When a session's native memory is released (after Dispose and the last in-flight run), Starlings.Runtime.Onnx calls glibc's malloc_trim(0), so a process that loads and unloads the model returns the freed memory instead of keeping it resident. It never runs on the request path and changes no allocator setting. Opt out per session with OnnxSessionOptions.TrimNativeHeapOnRelease = false, or for the whole process with the AppContext switch Starlings.Runtime.Onnx.DisableNativeHeapTrim (details in the Starlings.Runtime.Onnx README).
  • Dedicated run threads on CPU (default). E5Encoder.Create gives its CPU sessions the runtime's dedicated run threads (OnnxSessionOptions.DedicatedRunThreads, null by default, means on here; set it to false to run on the calling thread; a session passed to the E5Encoder constructor keeps its own setting). Each run allocates and frees memory with malloc on the thread that calls ONNX Runtime, and glibc keeps freed memory resident at the top of that thread's heap. Callers from the .NET thread pool rotate, so that memory spread over 1 to 8 heaps holding 4 to 139 MiB after unloading in the hosted one-hour soak, and one of the six hosted soaks of 2026-10-16 failed the growth rule (unloaded growth 58 MiB against 32, run 38913307558). On a fixed set of threads (at most one per processor, started only when all are busy) the same heaps are reused: 16 to 62 MiB in three soaks out of three. More concurrent calls than processors queue instead of oversubscribing the CPU, and a token cancelled while a call is queued takes effect when it reaches a thread. Embeddings are bit-identical either way (a test checks it), and request latency in the soak was unchanged (p50 17.4 ms before, 16.9 and 17.2 ms after, on the same CPU model). ONNX Runtime's CPU memory arena keeps its default (on). Evidence: e5 soak reliability.
  • Transparent huge pages. The qualification soaks ran with /sys/kernel/mm/transparent_hugepage/enabled set to madvise (the Ubuntu default). On hosts set to always, the kernel's khugepaged fills in partly used 2 MiB heap ranges, so resident memory can look larger and rise over time without any leak.
  • No MALLOC_MMAP_THRESHOLD_ or other MALLOC_* setting is required for this profile.

Evidence: scheduled regression fix.

Qualification evidence

Product Compatible and additional computed target framework versions.
.NET 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Starlings.Models.E5:

Package Downloads
Starlings.Models.E5.Extensions.AI

Microsoft.Extensions.AI IEmbeddingGenerator<string, Embedding<float>> over the multilingual-e5-small text embedder (Starlings.Models.E5): explicit e5 input-type prefixes, batching, usage, node-boundary cancellation, model metadata and DI registration. Maturity is per model profile (Stable, Preview or Experimental): see the README, generated from the model registry.

Starlings.Models.E5.MLNet

ML.NET estimator and transformer for the multilingual-e5-small text embedder (Starlings.Models.E5): a text column to a fixed-size Vector<Single, 384> embedding column and an optional token-count column, with one inference per row however many getters are requested, explicit e5 prefixes and optional look-ahead batching. Pipeline composition only: saving the transformer is unsupported (it throws); the model is resolved from its asset lock at run time. Maturity is per model profile (Stable, Preview or Experimental): see the README, generated from the model registry.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.3.0 99 10/19/2026
0.2.0 164 10/17/2026
0.1.0-preview.6 99 10/14/2026