Skip to content

Named-Entity Recognition

Detect named entities (people, organisations, locations, dates, money amounts, emails, phones, URLs, plus caller-supplied custom labels) in extracted text. Result populates ExtractedDocument.entities.

Backend Cargo feature When to use Status
Onnx (xberg-gliner) ner-onnx High throughput, local inference, deterministic Available.
Llm (liter-llm) ner-llm Domain-specific zero-shot labels, any of 165 providers Available today.

The WASM build detects entities inside the page, with no server round-trip. This is a direct API rather than a backend selection: load the GLiNER2 weights once and call the model.

const model = await NerModel.load({ weights, tokenizer, encoderConfig });
const entities = await model.detect(text, { categories: ["person", "organization"] });
model.free();

Weights are not embedded in the .wasm, so the host fetches the safetensors, tokenizer, and encoder config and passes the bytes. The model stays resident, so repeated detect calls do not re-parse it. Inference is synchronous on a single-threaded target — run it in a Web Worker if main-thread responsiveness matters.

free() is optional: like every class in the package it is reclaimed by the garbage collector. Calling it is still worth it here, because GC timing is unobservable and WASM linear memory only ever grows, so a few hundred megabytes of weights can sit allocated long after the last detect.

The ONNX backend downloads supported Xberg GLiNER aliases and catalog ids from xberg-io/gliner-models. The runtime consumes exported ONNX artifacts and tokenizer files; it does not load arbitrary source PyTorch model repositories. If the artifact repository is private or not publicly readable, authenticate with Hugging Face using credentials supported by hf-hub before warming the cache or running inference.

  • You need entity tags attached to extracted text for retrieval, faceting, or compliance review.
  • You need PII categories surfaced for downstream redaction (NER pairs with the redaction post-processor — see Redaction & Anonymisation).
  • You need zero-shot labelling against caller-supplied categories (“Treatment”, “Vessel”, “Product”) that fall outside the GLiNER taxonomy.
  • You only need regex-detectable PII (emails, phones, IBANs, SSNs). The redaction pattern engine is 1000× cheaper. See Redaction & Anonymisation.
  • You want sub-100ms latency on a hot path with a large LLM. Prefer the ONNX backend (ner-onnx) for deterministic local inference.
Python
import asyncio
from xberg import ExtractInput, extract, ExtractionConfig, NerConfig, LlmConfig
async def main() -> None:
config = ExtractionConfig(
ner=NerConfig(
backend="llm",
llm=LlmConfig(model="openai/gpt-4o-mini"),
),
)
result = await extract(ExtractInput(uri="contract.pdf"), config)
for entity in result.results[0].entities or []:
print(f"{entity.category}: {entity.text} (confidence={entity.confidence})")
asyncio.run(main())

Pass arbitrary labels via NerConfig.custom_labels. The LLM backend folds each label into the structured-output schema; the ONNX backend uses GLiNER’s native zero-shot inference.

Python
from xberg import ExtractionConfig, NerConfig, LlmConfig
config = ExtractionConfig(
ner=NerConfig(
backend="llm",
llm=LlmConfig(model="openai/gpt-4o-mini"),
custom_labels=["Treatment", "Vessel", "Product"],
),
)

Custom hits surface as EntityCategory::Custom(label) in the resulting Entity stream. Casing of the supplied label is preserved.

ExtractedDocument.entities is Option<Vec<Entity>>, populated when NER ran and produced at least one detection. JSON shape:

{
"entities": [
{ "category": "person", "text": "Ada Lovelace", "start": 42, "end": 54, "confidence": 0.93 },
{ "category": { "custom": "Treatment" }, "text": "metformin", "start": 120, "end": 129, "confidence": 0.81 }
]
}

Byte offsets refer to result.content. When the redaction post-processor rewrites the document, NER offsets are recomputed against the redacted text — use the audit trail in result.redaction_report to reconstruct positions in the original.

EntityCategory Description
Person Person names.
Organization Organisations, companies, institutions.
Location Geographic locations.
Date Date mentions.
Time Time-of-day mentions.
Money Monetary amounts with currency.
Percent Percentages.
Email Email addresses.
Phone Phone numbers.
Url URLs.
Custom(label) Caller-supplied zero-shot label.

When backend = "llm", configure the model via NerConfig.llm. The API-key precedence chain matches LLM Integration:

  1. NerConfig.llm.api_key
  2. XBERG_LLM_API_KEY
  3. Per-provider env var (OPENAI_API_KEY, ANTHROPIC_API_KEY, …)

Local engines (Ollama, LM Studio, vLLM) need no key.

  • The LLM backend’s accuracy depends on the chosen model. Use gpt-4o-mini or larger for production NER.