Rust engine: github.com/dariofinardi/gliner25-rs
The crate that consumes these files, along with the exporter that produced them
and the script that verifies them against PyTorch.
Converted and published by Jugaad s.r.l., which uses it in
production inside Edito and Omissis.
What is in here
GLiNER2.5 uses the boundary architecture, which cannot be traced into a single
ONNX graph: it iterates over a variable number of schema queries and a variable
number of proposed candidates. It is therefore exported as a small pipeline of
fragments, orchestrated by the host:
C is constant at 192 (pool_size): the candidate pool is shared across all
queries. Decoding — sigmoid, per-query threshold, overlap policy, ranking — is
left to the host. boundary_manifest.json carries everything the runtime needs
to do it: pool size, buckets, overlap policy, whether the abstention and count
heads are present.
Precision variants
Suffix
I/O
Use for
_fp32
FP32
universal fallback, OpenVINO, CPU
_fp16
FP32 (keep_io_types=True)
CoreML, which demands FP32 I/O
_fp16_iobinding
FP16
CUDA, ROCm, QNN with IOBinding
You only need one variant. A full FP16 set is about 540 MB; FP32 is about 1.1 GB.
Length buckets
The boundary heads have a staticnum_words, because torch.export
specialises it: the candidate-pool builder contains a Python loop over a
symbolic dimension. One head is therefore exported per length bucket — 64, 128,
256 and 512 words — and the runtime picks the smallest that fits the text,
padding the remainder with text_mask = 0.
This costs almost nothing: a head is a few MB against 530 MB of encoder, and
static shapes are what TensorRT, QNN and IOBinding prefer. Masked padding is
verified to be transparent — for the same real words, padding to a larger
bucket, even with random noise in the padded rows, yields the same candidate
set and probabilities to within 5e-07.
Texts longer than 512 words must be chunked. The encoder is mDeBERTa-v3-base
with max_position_embeddings = 512, so that is the practical ceiling anyway.
Parity with PyTorch
Every fragment was compared against its PyTorch counterpart across all three
precision variants, with tolerances relative to each tensor's magnitude:
Fragment
FP32
FP16
encoder
1.8e-06
1.5e-03
routed_gather
0 (exact)
2.8e-04
classifier
1.8e-07
2.0e-04
boundary_head_L* candidate pool
identical
identical (one bucket: 99.5%)
boundary_head_L* probabilities
1.4e-06
2.5e-03
Reproduce with verify_parity.py from the Rust repository.
Note on comparing candidates: pool order carries no meaning. It comes from
an argsort over frequently near-tied scores, and sort stability is exactly
what the export removes — ONNX has no stable Sort, and aten.sort.stable has no
translation. Under FP16 rounding permutes the ties while still selecting the
same candidates. Compare cand_indices as a set of (start, end) pairs, never
positionally.
Files
text
1encoder_{fp32,fp16,fp16_iobinding}.onnx 1060 / 531 / 531 MB
2boundary_head_L{64,128,256,512}_{variant}.onnx 0.7-4.8 MB each
3routed_gather_{variant}.onnx a few KB
4classifier_{fp32,fp16,fp16_iobinding}.onnx 4.5 / 2.3 / 2.3 MB
5boundary_manifest.json runtime configuration
6tokenizer.json 15.3 MB
The boundary_head_L*_fp32.onnx files keep their weights in a companion
.onnx.data file — download both, and keep them side by side.
Usage
rust
1usegliner25_core::{BoundaryConfig,BoundaryEngine,SchemaTask};23gliner25_core::init("my-app");45letmut engine =BoundaryEngine::new(BoundaryConfig::new("gliner2.5-multi-v1-onnx"))?;6let tasks =vec![SchemaTask::Entities(vec![7"person".into(),"organization".into(),"location".into(),8])];910for m in engine.extract("Mario Rossi works at Apple in Cupertino.",&tasks)?.mentions {11println!("{} -> {} ({:.1}%)", m.text, m.field, m.score *100.0);12}
gliner25-rs is a Cargo workspace: gliner25-core is the engine, gliner25
adds schema families — splitting a wide schema into groups of related labels and
merging the results, which is the documented remedy for labels interfering with
each other when many are passed at once.
The engine detects the architecture and the best precision for the platform on
its own. See the repository for
the exporter, the parity checker and the design notes.
Credits and license
The model is the work of the Fastino team; see the
original card below, reproduced unchanged. Apache-2.0, as upstream.
The ONNX conversion and the Rust engine are by Dario Finardi, published by
Jugaad s.r.l. — edito-pdf.com.
Original model card
Reproduced from fastino/gliner2.5-multi-v1.
The Python snippets below describe the PyTorch checkpoint, not this ONNX build.
GLiNER2.5 Multi: Unified Schema-Based Information Extraction
Extract entities, classify text, parse structured records, score span attributes, and extract relations — all in one boundary architecture.
GLiNER2.5 Multi is the multilingual boundary checkpoint. It is built on mDeBERTa-v3-base and is the default choice when you need entities, classification, records, and relations in one model across languages. Load it with AutoExtractor: the checkpoint's architecture field selects BoundaryExtractor automatically.
1model = AutoExtractor.from_pretrained(2"fastino/gliner2.5-multi-v1",3 map_location="cuda",# or "cpu" / "mps"4 quantize=True,# fp16 weights on GPU5compile=True,# torch.compile after the first tracing call6)7print(type(model).__name__,next(model.parameters()).device)8# BoundaryExtractor cuda:0
Always check result.feasible. False means the hard constraints could not be satisfied (distinct from “the text contains no facts”).
python
1for rel in result.relations:2 head = result.entity(rel.head)3 tail = result.entity(rel.tail)4print(f"{head.text} -{rel.type}-> {tail.text}")5# Alice -works_for-> Acme6# Bob -works_for-> Acme7# Acme -located_in-> Paris
Span attributes: people with sentiment
Attributes are span-conditioned. The model finds entities first, then scores attribute labels at those exact spans. They are not extra entity types and they are not document-level classification.
applies_to=["person"] keeps sentiment off other entity types. qualify_labels=True encodes model-facing queries as sentiment: positive while returning the short label positive.
Restrict sentiment to people while still extracting companies:
Span length: any length that fits in the encoded window (max_len=4096)
Encoder:microsoft/mdeberta-v3-base
Parameters: 287M
Weights: ~594 MB (mostly FP16)
Language: Multilingual
Heads enabled: classification, records (enable_records=True), relations (enable_relations=True)
Overlap default:flat (weighted interval scheduling); override per call with overlap_policy
Input / output: text → entities, labels, span attributes, records, and relation edges
Do not load this checkpoint with GLiNER2 / SpanExtractor. Those classes expect the legacy span architecture.
Citation
If you use this model, please cite:
bibtex
1@misc{zaratiana2025gliner2efficientmultitaskinformation,
2 title={GLiNER2: An Efficient Multi-Task Information Extraction System with Schema-Driven Interface},
3 author={Urchade Zaratiana and Gil Pasternak and Oliver Boyd and George Hurn-Maloney and Ash Lewis},
4 year={2025},
5 eprint={2507.18546},
6 archivePrefix={arXiv},
7 primaryClass={cs.CL},
8 url={https://arxiv.org/abs/2507.18546},
9}