anno/backends/mod.rs
1//! NER backend implementations.
2//!
3//! Each backend implements the `Model` trait for consistent usage.
4//!
5//! # Architecture
6//!
7//! ```text
8//! ┌─────────────────────────────────────────────────────┐
9//! │ Layer 3: ML Backends (feature-gated) │
10//! │ │
11//! │ Zero-Shot NER (any entity type): │
12//! │ - GLiNER: Bi-encoder span classification │
13//! │ - NuNER: Token classification (arbitrary length) │
14//! │ │
15//! │ Complex Structures (nested/discontinuous): │
16//! │ - W2NER: Word-word relation grids │
17//! │ │
18//! │ Traditional (fixed types): │
19//! │ - BertNEROnnx: Sequence labeling │
20//! ├─────────────────────────────────────────────────────┤
21//! │ Layer 2: HeuristicNER (zero deps) │
22//! │ Person/Org/Location via heuristics │
23//! ├─────────────────────────────────────────────────────┤
24//! │ Layer 1: RegexNER (zero deps) │
25//! │ Date/Time/Money/Email/URL/Phone │
26//! └─────────────────────────────────────────────────────┘
27//! ```
28//!
29//! # Backend Comparison
30//!
31//! | Backend | Feature | Zero-Shot | Nested | Notes |
32//! |---------|---------|-----------|--------|-------|
33//! | `StackedNER` | - | No | No | Composable with any backend |
34//! | `RegexNER` | - | No | No | Structured entities only |
35//! | `HeuristicNER` | - | No | No | Simple heuristic baseline |
36//! | `GLiNER` | `onnx` | Yes | No | Span-based |
37//! | `NuNER` | `onnx` | Yes | No | Token-based |
38//! | `W2NER` | `onnx` | No | Yes | Grid-based |
39//! | `BertNEROnnx` | `onnx` | No | No | Traditional fixed-label NER |
40//!
41//! # When to Use What
42//!
43//! - **Default choice**: `NERExtractor::best_available()` (picks the best available backend at
44//! runtime, based on enabled features)
45//! - **Zero deps**: `StackedNER::default()` - no ML, good baseline
46//! - **Hybrid approach**: `StackedNER` with ML backends - combine ML accuracy with pattern speed
47//! - **Custom types**: `GLiNER` or `NuNER` - zero-shot, any entity type
48//! - **Nested entities**: `W2NER` - handles overlapping spans
49//! - **Structured data**: `RegexNER` - dates, emails, money
50//!
51//! # Backend Combination Design Space
52//!
53//! Two approaches for combining multiple backends:
54//!
55//! | Combiner | Execution | Conflict Resolution | Best For |
56//! |----------|-----------|---------------------|----------|
57//! | [`StackedNER`] | Sequential (cascade) | Priority/LongestSpan/HighestConf | Production, latency |
58//! | [`EnsembleNER`] | Parallel (all) | Weighted voting + agreement | Maximum accuracy |
59//!
60//! **StackedNER** runs backends in layer order. Earlier layers claim spans first.
61//! Good for: fast execution, structured patterns + ML fill-in.
62//!
63//! **EnsembleNER** runs ALL backends, groups overlapping spans into conflict clusters,
64//! and resolves via weighted voting with type-conditioned weights and agreement bonuses.
65//! Good for: maximum accuracy when latency allows.
66//!
67//! Both accept any `Model` implementation - they're fully composable with ML backends.
68//!
69//! # Quick Start
70//!
71//! Zero-dependency default (Pattern + Heuristic):
72//!
73//! ```rust
74//! use anno::{Model, StackedNER};
75//!
76//! let ner = StackedNER::default();
77//! let entities = ner.extract_entities("Dr. Smith charges $100/hr", None).unwrap();
78//! ```
79//!
80//! Custom stack with pattern + heuristic:
81//!
82//! ```rust
83//! use anno::{Model, RegexNER, HeuristicNER, StackedNER};
84//! use anno::backends::stacked::ConflictStrategy;
85//!
86//! let ner = StackedNER::builder()
87//! .layer(RegexNER::new())
88//! .layer(HeuristicNER::new())
89//! .strategy(ConflictStrategy::LongestSpan)
90//! .build();
91//! ```
92//!
93//! **StackedNER is fully composable** - you can combine ML backends with pattern/heuristic layers:
94//!
95//! ```rust,no_run
96//! #[cfg(feature = "onnx")]
97//! {
98//! use anno::{Model, StackedNER, GLiNEROnnx, RegexNER, HeuristicNER};
99//! use anno::backends::stacked::ConflictStrategy;
100//!
101//! // ML-first: ML runs first, then patterns fill gaps
102//! let ner = StackedNER::with_ml_first(
103//! Box::new(GLiNEROnnx::new("onnx-community/gliner_small-v2.1").unwrap())
104//! );
105//!
106//! // ML-fallback: patterns/heuristics first, ML as fallback
107//! let ner = StackedNER::with_ml_fallback(
108//! Box::new(GLiNEROnnx::new("onnx-community/gliner_small-v2.1").unwrap())
109//! );
110//!
111//! // Custom stack: any combination of backends
112//! let ner = StackedNER::builder()
113//! .layer(RegexNER::new()) // High-precision structured entities
114//! .layer_boxed(Box::new(GLiNEROnnx::new("onnx-community/gliner_small-v2.1").unwrap())) // ML layer
115//! .layer(HeuristicNER::new()) // Quick named entities
116//! .strategy(ConflictStrategy::HighestConf) // Resolve conflicts by confidence
117//! .build();
118//! }
119//! ```
120
121// Always available (zero deps beyond std)
122/// BiLSTM + CRF NER - neural baseline from 2015-2018.
123///
124/// Bidirectional LSTM with Conditional Random Field output layer.
125/// The dominant neural NER architecture before BERT/transformers.
126pub mod bilstm_crf;
127/// Box embeddings for geometric coreference resolution.
128pub mod box_embeddings;
129/// Training system for box embeddings.
130///
131/// This is the canonical training implementation. The [matryoshka-box](https://github.com/arclabs561/matryoshka-box)
132/// research project extends this with matryoshka-specific features (variable dimensions, etc.).
133pub mod box_embeddings_training;
134pub mod catalog;
135pub mod crf;
136pub mod encoder;
137pub mod event_extractor;
138pub mod extractor;
139pub mod heuristic;
140pub mod inference;
141/// Label prompt normalization for zero-shot NER systems.
142pub mod label_prompt;
143pub mod lexicon;
144pub mod nuner;
145pub mod ort_compat;
146pub mod pattern_config;
147pub mod regex;
148/// Language-aware routing for automatic backend selection.
149pub mod router;
150pub mod rule;
151/// Shared span-tensor utilities for span-based NER backends (GLiNER/NuNER family).
152pub mod span_utils;
153pub mod stacked;
154pub mod tplinker;
155pub mod w2ner;
156
157/// Ensemble NER - weighted voting across multiple backends.
158///
159/// Unlike `StackedNER` (priority-based layers), `EnsembleNER` collects
160/// candidates from ALL backends and resolves conflicts via weighted voting
161/// with agreement bonuses.
162pub mod ensemble;
163
164/// Hidden Markov Model NER - classical statistical approach.
165///
166/// Implements HMM-based sequence labeling, the dominant approach from the 1990s
167/// before CRFs. Useful as a baseline and for understanding NER history.
168pub mod hmm;
169
170/// Middleware pipeline for preprocessing and postprocessing.
171///
172/// Provides a chain-of-responsibility pattern for transforming text before
173/// entity extraction and filtering/enriching entities afterward.
174pub mod middleware;
175
176/// Streaming NER for incremental entity extraction.
177///
178/// Iterator-based API for processing large documents in chunks,
179/// with backpressure support and boundary handling.
180pub mod streaming;
181
182/// Chunking helpers for long text.
183///
184/// Always provides a lightweight rule-based chunker (paragraph boundaries + size limits + overlap).
185/// With the `semantic-chunking` feature enabled, adds a sentence-similarity chunker (no embeddings).
186pub mod semantic_chunking;
187
188// Burn ML framework (training + inference)
189#[cfg(feature = "burn")]
190pub mod burn;
191
192// Advanced backends
193#[cfg(feature = "onnx")]
194pub mod albert;
195#[cfg(feature = "onnx")]
196pub mod deberta_v3;
197#[cfg(feature = "onnx")]
198pub mod gliner_poly;
199pub mod universal_ner;
200
201// LLM-based NER prompting (CodeNER-style)
202pub mod llm_prompt;
203
204// Demonstration selection for few-shot NER (CMAS-inspired)
205pub mod demonstration;
206
207// GLiNER via ONNX (uses same feature as other ONNX models)
208
209// ONNX implementations
210#[cfg(feature = "onnx")]
211pub mod gliner_onnx;
212
213#[cfg(feature = "onnx")]
214pub mod onnx;
215
216// Pure Rust via Candle
217#[cfg(feature = "candle")]
218pub mod candle;
219
220#[cfg(feature = "candle")]
221pub mod encoder_candle;
222
223#[cfg(feature = "candle")]
224pub mod gliner_candle;
225
226#[cfg(feature = "candle")]
227pub mod gliner_pipeline;
228
229// GLiNER2 multi-task extraction (ONNX or Candle)
230#[cfg(any(feature = "onnx", feature = "candle"))]
231pub mod gliner2;
232
233// Production infrastructure
234#[cfg(feature = "production")]
235pub mod async_adapter;
236
237#[cfg(all(feature = "production", feature = "onnx"))]
238pub mod session_pool;
239
240// Model warmup for cold-start mitigation
241pub mod warmup;
242
243// T5-based coreference resolution
244#[cfg(feature = "onnx")]
245pub mod coref_t5;
246
247// Graph-based coreference (iterative refinement)
248pub mod graph_coref;
249
250// Mention-ranking coreference (Bourgois & Poibeau 2025 inspired)
251pub mod mention_ranking;
252
253// Re-exports (always available)
254pub use bilstm_crf::BiLstmCrfNER;
255pub use crf::CrfNER;
256pub use ensemble::EnsembleNER;
257pub use event_extractor::{Event, EventExtractor, RuleBasedEventExtractor};
258pub use extractor::{BackendType, NERExtractor};
259pub use heuristic::HeuristicNER;
260pub use lexicon::LexiconNER;
261pub use nuner::NuNER;
262pub use regex::RegexNER;
263pub use router::AutoNER;
264pub use stacked::{ConflictStrategy, StackedNER};
265
266/// Backwards compatibility alias for `HeuristicNER`.
267///
268/// The name "StatisticalNER" was misleading since it doesn't use
269/// statistical/probabilistic methods like CRF or HMM - it uses
270/// capitalization and context heuristics.
271#[deprecated(
272 since = "0.3.0",
273 note = "Use HeuristicNER instead - StatisticalNER was misleading"
274)]
275pub type StatisticalNER = HeuristicNER;
276pub use tplinker::TPLinker;
277pub use w2ner::{W2NERConfig, W2NERRelation, W2NER};
278
279// Advanced backends
280#[cfg(feature = "onnx")]
281pub use albert::ALBERTNER;
282#[cfg(feature = "onnx")]
283pub use deberta_v3::DeBERTaV3NER;
284#[cfg(feature = "onnx")]
285pub use gliner_poly::GLiNERPoly;
286pub use universal_ner::UniversalNER;
287
288// Backwards compatibility
289#[allow(deprecated)]
290pub use stacked::{CompositeNER, LayeredNER, TieredNER};
291
292#[allow(deprecated)]
293pub use rule::RuleBasedNER;
294
295// Re-exports (feature-gated)
296#[cfg(feature = "onnx")]
297pub use gliner_onnx::GLiNEROnnx;
298
299#[cfg(feature = "onnx")]
300pub use onnx::BertNEROnnx;
301
302#[cfg(feature = "candle")]
303pub use candle::CandleNER;
304
305#[cfg(feature = "candle")]
306pub use encoder_candle::{EncoderArchitecture, EncoderConfig};
307
308#[cfg(feature = "candle")]
309pub use gliner_candle::GLiNERCandle;
310
311// GLiNER2 multi-task model
312#[cfg(any(feature = "onnx", feature = "candle"))]
313pub use gliner2::{
314 ClassificationResult, ClassificationTask, EntityTask, ExtractedStructure, ExtractionResult,
315 FieldType, GLiNER2, StructureTask, StructureValue, TaskSchema,
316};
317
318#[cfg(feature = "onnx")]
319pub use gliner2::GLiNER2Onnx;
320
321#[cfg(feature = "candle")]
322pub use gliner2::GLiNER2Candle;
323
324// Production infrastructure re-exports
325#[cfg(feature = "production")]
326pub use async_adapter::{batch_extract, batch_extract_limited, AsyncNER, IntoAsync};
327
328#[cfg(all(feature = "production", feature = "onnx"))]
329pub use session_pool::{GLiNERPool, PoolConfig, SessionPool};
330
331// T5 coreference
332#[cfg(feature = "onnx")]
333pub use coref_t5::{CorefCluster, T5Coref, T5CorefConfig};
334
335// Config re-exports (for quantization control)
336#[cfg(feature = "onnx")]
337pub use gliner_onnx::GLiNERConfig;
338
339#[cfg(feature = "onnx")]
340pub use onnx::BertNERConfig;
341
342// Warmup utilities (always available)
343pub use warmup::{warmup_model, warmup_with_callback, WarmupConfig, WarmupResult};
344
345// Box embeddings for geometric coreference
346pub use box_embeddings::{
347 acquisition_roles, interaction_strength, BoxCorefConfig, BoxEmbedding, BoxVelocity, Conflict,
348 GumbelBox, TemporalBox, UncertainBox,
349};
350
351// Box embedding training (canonical implementation; matryoshka-box extends with research features)
352pub use box_embeddings_training::{
353 coref_documents_to_training_examples, split_train_val, BoxEmbeddingTrainer, TrainingConfig,
354 TrainingExample,
355};
356
357// Coreference resolution trait (from anno-core, always available)
358pub use anno_core::CoreferenceResolver;
359
360// Classical HMM NER (zero deps)
361pub use hmm::{HmmConfig, HmmNER};
362
363// Streaming NER utilities
364pub use streaming::{ChunkConfig, EntityIterator, StreamingExtractor};
365
366// Middleware pipeline
367pub use middleware::{
368 FilterByConfidence, FilterByType, HookedPipeline, Middleware, MiddlewareContext,
369 NormalizeWhitespace, Pipeline as MiddlewarePipeline, RemoveOverlaps,
370};
371
372// Burn ML framework (trainable)
373#[cfg(feature = "burn")]
374pub use burn::{BurnConfig, BurnNER};
375
376// Simple resolvers for evaluation pipelines (eval feature only)
377// NOTE: These live in eval/ and are for evaluation, not production.
378// For production coreference, use `MentionRankingCoref` above.
379#[cfg(any(feature = "analysis", feature = "eval"))]
380pub use crate::eval::coref_resolver::{BoxCorefResolver, CorefConfig, SimpleCorefResolver};
381#[cfg(all(feature = "eval", feature = "discourse"))]
382pub use crate::eval::coref_resolver::{DiscourseAwareResolver, DiscourseCorefConfig};