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
<PackageReference Include="Starlings.Models.E5" Version="0.3.0" />
<PackageVersion Include="Starlings.Models.E5" Version="0.3.0" />
<PackageReference Include="Starlings.Models.E5" />
paket add Starlings.Models.E5 --version 0.3.0
#r "nuget: Starlings.Models.E5, 0.3.0"
#:package Starlings.Models.E5@0.3.0
#addin nuget:?package=Starlings.Models.E5&version=0.3.0
#tool nuget:?package=Starlings.Models.E5&version=0.3.0
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
Disposeand the last in-flight run),Starlings.Runtime.Onnxcalls glibc'smalloc_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 withOnnxSessionOptions.TrimNativeHeapOnRelease = false, or for the whole process with theAppContextswitchStarlings.Runtime.Onnx.DisableNativeHeapTrim(details in theStarlings.Runtime.OnnxREADME). - Dedicated run threads on CPU (default).
E5Encoder.Creategives its CPU sessions the runtime's dedicated run threads (OnnxSessionOptions.DedicatedRunThreads,nullby default, means on here; set it tofalseto run on the calling thread; a session passed to theE5Encoderconstructor keeps its own setting). Each run allocates and frees memory withmallocon 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/enabledset tomadvise(the Ubuntu default). On hosts set toalways, 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 otherMALLOC_*setting is required for this profile.
Evidence: scheduled regression fix.
Qualification evidence
- G0 provenance: the 0.1.0-preview.1 record, confirmed anonymously in the 2026-10-14 provenance check.
- G1, G2 and G3 on the P-030 release sets: release-set gates.
- G4 one-hour soak: 2026-10-14. G5 matched benchmark: 2026-10-15.
- G6 distribution and G7 integration: G6, G7.
- Regression support: the weekly qualification workflow (jobs
release-gatesandsoak), green in run 38822625245.
| Product | Versions 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. |
-
net10.0
- Starlings.Acquisition (= 0.3.0)
- Starlings.Runtime.Onnx (= 0.3.0)
- Starlings.Tokenizers (= 0.3.0)
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 |