Starlings.Models.Gliner2 0.3.0

dotnet add package Starlings.Models.Gliner2 --version 0.3.0
                    
NuGet\Install-Package Starlings.Models.Gliner2 -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.Gliner2" 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.Gliner2" Version="0.3.0" />
                    
Directory.Packages.props
<PackageReference Include="Starlings.Models.Gliner2" />
                    
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.Gliner2 --version 0.3.0
                    
#r "nuget: Starlings.Models.Gliner2, 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.Gliner2@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.Gliner2&version=0.3.0
                    
Install as a Cake Addin
#tool nuget:?package=Starlings.Models.Gliner2&version=0.3.0
                    
Install as a Cake Tool

Starlings.Models.Gliner2

English entity extraction with fastino/gliner2-base-v1 at revision f9634218e53580c56edf0de97ca1a7d3f1c2354e (GLiNER2 2.0.0, source 55656fbf), on ONNX Runtime 1.30.0, in-process and offline after an explicit acquisition. You name the entity labels; the model returns labelled spans with offsets and confidences. The package is versioned 0.x, so the API may still change between minor versions; maturity is per profile (below).

  • GLiNER2's text handling is ported with Python semantics (normalization, the regular-expression word splitter, str.lower()), and the schema prefix is encoded with the owned tokenizer Starlings.Tokenizers (profile gliner2-base-v1-unigram). No Microsoft.ML.Tokenizers, no native tokenizer, no Python.
  • One ONNX graph (token ids and routing positions to count and span logits); GLiNER2's count gate, threshold, overlap suppression and result order in C#.
  • The package ships the asset lock (sizes and SHA-256), not the 838 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
gliner2-base-v1/onnx-fp32-cpu stable linux-x64 cpu: supported; linux-x64 cuda: supported; windows-x64 cpu: experimental
gliner2-base-v1/onnx-fp32-cuda stable linux-x64 cuda: supported; windows-x64 cuda: untested

Install

dotnet add package Starlings.Models.Gliner2
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)

Use

using Starlings.Models.Gliner2;

await Gliner2ModelAssets.DownloadAsync();                   // once, explicit, needs the network; verifies SHA-256
Gliner2ModelFiles files = Gliner2ModelAssets.OpenLocal();   // offline, verified, never downloads
using var extractor = Gliner2EntityExtractor.Create(files);

var result = extractor.Extract(
    "Apple CEO Tim Cook announced the iPhone 15 in Cupertino.",
    ["company", "person", "product", "location"],
    threshold: 0.5f);
foreach (var e in result.Entities)
{
    Console.WriteLine($"{e.Label}: {e.Text} [{e.Start}, {e.End}) {e.Confidence:F3}");
}

Batching options

ExtractBatch and RunModel split a batch (one label set) into ONNX Runtime calls, each right-padded to its longest text. Gliner2ExtractorOptions controls the split:

Option Default Effect
MaxBatchSize 8 Texts per call at most (GLiNER2's default batch_size). Chunks are consecutive texts in input order.
MaxBatchTokens null (no budget) Padded tokens (texts x longest text, schema prefix included) per call at most. A chunk closes before it would exceed the budget; a text longer than the budget runs alone. Experimental (batch.token_budget).
SortByLength false Chunks texts in ascending token count (ties in input order) so each call pads less; results stay in input order. Experimental (batch.sort_by_length).

The defaults are the qualified behavior, with outputs unchanged bit for bit. With SortByLength, which texts share a call depends on the whole batch, so span logits can differ from the default by up to the profile's padded-batch-vs-single bound (padded_batch_vs_single_span_logits_relative); the CPU tests saw no difference and identical entities on the 54 in-contract fixture cases in random orders. On CUDA, calls stay serialized (SerializeNativeCalls); the options only change how one request is chunked.

Stable contract

This is the contract of profile gliner2-base-v1/onnx-fp32-cpu. Its Stable label commits to it under the support policy. The statuses are the registry manifest's and Gliner2Capabilities.Profiles, which a test keeps equal key for key.

Item Contract
Profile gliner2-base-v1/onnx-fp32-cpu (registry manifest model-pipeline/registry/gliner2-base-v1/onnx-fp32-cpu/manifest.yaml).
Model fastino/gliner2-base-v1 at f9634218e53580c56edf0de97ca1a7d3f1c2354e; GLiNER2 source 55656fbfa01d3d4a77485e1a1eeeaf682990ccdf. The ONNX graph is Starling's reproducible export (onnx/gliner2-entities-fp32.onnx, digest in Gliner2ModelAssets), downloaded with no token from the public repository Starlinghq/gliner2-base-v1-onnx at c02c3350.
Tokenizer Starlings.Tokenizers 0.3.0, profile gliner2-base-v1-unigram (fast-tokenizer semantics; byte-fallback pieces map to [UNK] as GLiNER2 does).
Runtime ONNX Runtime 1.30.0 (the application adds Microsoft.ML.OnnxRuntime 1.30.0 CPU natives), fp32, .NET 10.
Supported platform Linux x64, CPU, with the deployment setting below. 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 short soak (with the setting) on GitHub-hosted ubuntu-latest x64.
Linux deployment requirement Start the process with MALLOC_MMAP_THRESHOLD_=65536. The G4 qualification soak passed with it; see Memory on Linux.
Input English text and an ordered set of entity labels, threshold (default 0.5). One label set per call (also per batch).
Output Entities with label, text, confidence and offsets into the original text: Start/End in UTF-16 code units, and ScalarStart/ScalarEnd in Unicode scalar values. Text == input[Start..End] always. Order: labels in request order, then descending confidence. Per label, confidence-first suppression of overlapping spans; spans of different labels may overlap.
Numerical bound The protected tolerances of the profile (span logits relative 1e-4; order and threshold ties within 2e-5 of the reference, per the maintainer decision of 2026-10-10). Release set: 1.78e-5.
Task quality WNUT-17 test, 300 sentences: exact micro F1 at least 0.352 and partial micro F1 at least 0.403 (budget); measured 0.3626 and 0.4134, equal to the PyTorch reference.
Supported Entity extraction with labels and threshold, the staged API, original-text offsets, batch, cancellation, concurrent calls, offline asset resolution and explicit download, Native AOT (CPU natives).
Limits At most 512 tokens, schema prefix included; longer texts throw Gliner2InputTooLongException, and nothing is truncated. Spans up to 8 words. The model files (838 MB) are acquired explicitly; inference never downloads.
Outside the Stable contract Label descriptions and the closed count gate: experimental (proposed capability cases, not yet accepted fixtures). CUDA is the separate profile gliner2-base-v1/onnx-fp32-cuda, with its own contract (Stable contract: CUDA). Windows x64 CPU: experimental (CI builds, runs the weight-free tests and the clean-install drill without the model; no model run). macOS and ARM64: unsupported (no evidence).
Unsupported inputs over 512 tokens, unserialized concurrent calls on CUDA (wrong entities in 6 of 8 test runs), truncation, the max_len word cap, extract_long, GLiNER2's plain de-duplicated output, mixed label sets in one batch, classification, relation extraction, structured (JSON) extraction, per-label thresholds, overlap policies, the character splitter, non-English text, TF32, fp16, quantized graphs, streaming, persistence, training, Microsoft.Extensions.AI, ML.NET (no adapter exists).
Known issues Most entity scores saturate to exactly 1.0 in fp32, so near-ties make the order sensitive to backend noise. Upstream span offsets index the normalized text: a span that includes the appended . ends one past the original text (seen at threshold 0.0); the package maps such spans onto the input. With default glibc settings, memory retained by the allocator trips the soak's growth rule (below).

Stable contract: CUDA

This is the contract of profile gliner2-base-v1/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 Gliner2Capabilities.Profiles, which a test keeps equal key for key.

Item Contract
Profile gliner2-base-v1/onnx-fp32-cuda (registry manifest model-pipeline/registry/gliner2-base-v1/onnx-fp32-cuda/manifest.yaml). The same model, graph 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, 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.
Linux deployment requirement Start the process with MALLOC_MMAP_THRESHOLD_=65536, as on CPU (Memory on Linux). The G4 soak and the per-release soak smoke test run with it.
Concurrency Calls on one extractor are serialized on CUDA (Gliner2ExtractorOptions.SerializeNativeCalls, the CUDA default). Unserialized concurrent calls on one CUDA session are unsupported (wrong entities in 6 of 8 test runs).
Cancellation A CUDA run is never terminated mid-graph: it completes, then the call throws OperationCanceledException (#61). Latency is one chunk, about 50 ms for 8 long texts; a 4,096-text batch cancelled after 50 ms threw at 52 to 53 ms.
Numerical bound The CUDA profile's protected tolerances (span logits relative 1e-4; order and threshold ties within 2e-5). Release set: span logits 1.4e-5 relative, entities 30/30 from inputs and from text.
Task quality WNUT-17: exact micro F1 0.3626, partial micro F1 0.4134 (budget 0.352 and 0.403).
Performance G5 on an RTX 3090: inference 0.959 to 0.996 of the Python ONNX Runtime CUDA run; prepare and decode 0.6 to 1.5% of inference (budget 5%).
Supported Entity extraction with labels and threshold, the staged API, original-text offsets, batch, cancellation, serialized concurrent calls, offline asset resolution and explicit download.
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). Label descriptions and the closed count gate: experimental, as on CPU. Windows x64 CUDA: untested.

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

Micro-batching (experimental, opt-in)

Gliner2ExtractorOptions.MicroBatching (null by default: off) applies to ExtractAsync only; Extract, ExtractBatch and the staged methods never queue. Without it, ExtractAsync runs on the calling thread like Extract and returns a completed task. With it, each call's text is prepared and length-checked on the calling thread (an invalid text fails only its own call), then queued; one dispatcher thread runs the queued texts of concurrent callers that share a schema (the same labels in the same order and the same descriptions) in padded batches of at most MaxRows texts and MaxTokens padded token cells, and each caller decodes its spans with its own threshold. MaxBatchSize still splits each batch into graph calls, and the CUDA run gate still applies, so on CUDA serialized single calls become serialized batched calls. Outputs then depend on the concurrent traffic: equal to a single call within the profile's padded-batch tolerance, not bit for bit reproducible. Cancelling a queued call removes it; cancelling a dispatched one cancels only that caller's task (the batch keeps running for the others). A full queue (QueueCapacity) makes callers wait for a slot. Dispose faults queued calls with ObjectDisposedException and waits for a running batch. MicroBatchingStatistics reports batches, items, padding and queue and run time. Status: experimental (capability micro_batching): weight-free tests and one real-model test within the padded-batch tolerance on Linux x64 CPU; no benchmark adoption decision, no CUDA run and no soak yet.

Memory on Linux

Deployment requirement: set MALLOC_MMAP_THRESHOLD_=65536 in the environment of a Linux process that runs this profile (CPU or CUDA).

With default glibc settings, a process that loads and unloads the extractor repeatedly keeps freed native memory resident: about 1.2 to 1.5 GiB between loads, rising by about 65 to 90 MiB over the first half hour of cycling and then holding. This is allocator retention (glibc malloc's resident high-water mark and fragmentation), not a leak: with the threshold set, every unload returns what the load and its requests allocated. But the soak's growth rule, which is not changed for this, judges the default run: the one-hour default soak fit 81 to 82 MiB/h over the whole run and failed the rule, although the curve flattens after about 30 minutes. With MALLOC_MMAP_THRESHOLD_=65536 the footprint after unloading held at about 76 MiB, the loaded footprint was about 740 MiB lower, and the one-hour G4 soak passed.

Evidence: GLiNER2 soak growth (diagnosis, six controlled runs), G4 qualification soak.

Status

Profiles gliner2-base-v1/onnx-fp32-cpu (Linux x64 CPU) and gliner2-base-v1/onnx-fp32-cuda (Linux x64, RTX 3090, Stable since 2026-10-16) have passed every qualification gate, G0 to G7; their maturity is in the table above. 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. Evidence and reproduction: ai-dotnet/README.md and the catalog. License of the weights and the converted graph: Apache-2.0 (model card); GLiNER2 by Fastino AI, backbone initialised from microsoft/deberta-v3-base (MIT).

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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.3.0 88 10/19/2026
0.2.0 146 10/17/2026
0.1.0-preview.6 57 10/14/2026