# Backend Organization & Architecture

## Overview

The `anno` crate has **two distinct backend categories**:

1. **NER Backends** (Entity Extraction) - Pre-trained models, "off the shelf"
2. **Coreference Resolvers** (Entity Linking) - Rule-based + research implementations

## NER Backends: "Off the Shelf" Models

### Architecture: Layered by Capability

```text
┌─────────────────────────────────────────────────────────┐
│ Layer 3: ML Backends (feature-gated, pre-trained)      │
│                                                         │
│  Zero-Shot (any entity type at runtime):              │
│   • GLiNER (ONNX/Candle) - Bi-encoder, ~92% F1         │
│   • NuNER (ONNX) - Token classifier, ~92% F1           │
│                                                         │
│  Complex Structures (nested/discontinuous):            │
│   • W2NER (ONNX) - Word-word grids, ~88% F1            │
│                                                         │
│  Traditional (fixed types):                            │
│   • BertNEROnnx - Sequence labeling, ~86% F1           │
│   • CandleNER - Rust-native, ~74% F1                    │
│                                                         │
│  All require: model download, feature flags            │
├─────────────────────────────────────────────────────────┤
│ Layer 2: HeuristicNER (zero deps, always available)   │
│   • Person/Org/Location via capitalization + context   │
│   • ~60-70% F1, no model needed                        │
├─────────────────────────────────────────────────────────┤
│ Layer 1: RegexNER (zero deps, always available)        │
│   • Date/Time/Money/Email/URL/Phone                    │
│   • ~95%+ precision on structured entities            │
└─────────────────────────────────────────────────────────┘
```

### Key Characteristics

**All NER backends implement the `Model` trait:**

```rust
pub trait Model: Send + Sync {
    fn extract_entities(&self, text: &str, language: Option<&str>) -> Result<Vec<Entity>>;
    fn supported_types(&self) -> Vec<EntityType>;
    fn is_available(&self) -> bool;
    fn name(&self) -> &'static str;
}
```

**Organization by feature flags:**

- **Always available**: `RegexNER`, `HeuristicNER`, `StackedNER` (composite)
- **`onnx` feature**: `GLiNEROnnx`, `BertNEROnnx`, `NuNER`, `W2NER`
- **`candle` feature**: `GLiNERCandle`, `CandleNER`

**All are pre-trained models:**

- Models downloaded from Hugging Face (e.g., `onnx-community/gliner_small-v2.1`)
- Weights are fixed (not trained by us)
- We just run inference via ONNX Runtime or Candle

### Example: GLiNER Backend

```rust
// src/backends/gliner_onnx.rs
pub struct GLiNEROnnx {
    session: ort::Session,           // ONNX Runtime session
    tokenizer: tokenizers::Tokenizer, // Tokenizer
    config: GLiNERConfig,
}

impl Model for GLiNEROnnx {
    fn extract_entities(&self, text: &str, _: Option<&str>) -> Result<Vec<Entity>> {
        // 1. Tokenize text
        // 2. Run ONNX inference
        // 3. Decode spans
        // 4. Return entities
    }
}
```

**What we do:**
- Load pre-trained ONNX model
- Run inference
- Decode outputs

**What we don't do:**
- Train the model
- Modify weights
- Change architecture

## Coreference Resolvers: Rule-Based + Research

### Architecture: Different Purpose

Coreference resolution is **separate from NER**. It takes entities (from NER) and links them:

```text
Input:  "John went to the store. He bought milk."
         ^^^^ (entity)              ^^ (entity)
         
Output: Both entities get same canonical_id (they corefer)
```

### Current Resolvers

**1. SimpleCorefResolver** (`src/eval/coref_resolver.rs`)
- Rule-based, zero deps
- Pronoun resolution, fuzzy matching
- Always available

**2. DiscourseAwareResolver** (`src/eval/coref_resolver.rs`)
- Extends SimpleCorefResolver
- Handles abstract anaphora ("this problem" → event)
- Requires `discourse` feature

**3. BoxCorefResolver** (`src/eval/coref_resolver.rs`) ⭐ **NEW**
- Research implementation (not pre-trained)
- Uses box embeddings for geometric constraints
- Always available (no feature flag)

### Key Difference: Box Embeddings

**Box embeddings are NOT a pre-trained model:**

```rust
// src/backends/box_embeddings.rs
pub struct BoxEmbedding {
    pub min: Vec<f32>,  // We define these
    pub max: Vec<f32>,  // We compute these
}

// This is OUR implementation of the research idea
// Not a downloaded model
```

**What we do:**
- Implement the geometric representation (boxes)
- Encode logical invariants as geometry
- Provide utilities to convert vectors → boxes

**What we don't do:**
- Download pre-trained box embeddings
- Use someone else's implementation
- This is a **research contribution**, not "off the shelf"

## Organization Differences

### NER Backends

| Aspect | Details |
|--------|---------|
| **Source** | Pre-trained models from Hugging Face |
| **Implementation** | Inference wrappers (ONNX/Candle) |
| **Training** | Done by others (GLiNER team, NuMind, etc.) |
| **Feature Flags** | Required (`onnx`, `candle`) |
| **Files** | `gliner_onnx.rs`, `bert_onnx.rs`, etc. |
| **Trait** | `Model` (entity extraction) |

### Box Embeddings

| Aspect | Details |
|--------|---------|
| **Source** | Research implementation (ours) |
| **Implementation** | Full geometric representation |
| **Training** | Not yet implemented (future work) |
| **Feature Flags** | None (always available) |
| **Files** | `box_embeddings.rs` (types), `coref_resolver.rs` (resolver) |
| **Trait** | `CoreferenceResolver` (entity linking) |

## Why This Matters

### NER Backends: "Off the Shelf"

- **Advantage**: High accuracy (~90% F1), zero-shot capability
- **Disadvantage**: Requires model download, feature flags, network
- **Use case**: Production entity extraction

### Box Embeddings: Research Implementation

- **Advantage**: Encodes logical invariants, no model download
- **Disadvantage**: Not yet trained/optimized, experimental
- **Use case**: Research, theoretical grounding, future optimization

## Integration Points

### How They Work Together

```rust
// Step 1: Extract entities (NER backend - "off the shelf")
let ner = GLiNEROnnx::new("onnx-community/gliner_small-v2.1")?;
let entities = ner.extract_entities("John went to the store. He bought milk.", None)?;
// Returns: [Entity("John", Person), Entity("He", Person)]

// Step 2: Resolve coreference (Box embeddings - research)
let boxes = vectors_to_boxes(&embeddings, hidden_dim, None);
let resolver = BoxCorefResolver::new(BoxCorefConfig::default());
let resolved = resolver.resolve_with_boxes(&entities, &boxes);
// Returns: Both entities with same canonical_id
```

### File Organization

```
src/backends/
├── gliner_onnx.rs      # NER: Pre-trained GLiNER model wrapper
├── bert_onnx.rs        # NER: Pre-trained BERT model wrapper
├── regex.rs            # NER: Pattern-based (zero deps)
├── heuristic.rs        # NER: Heuristic-based (zero deps)
└── box_embeddings.rs   # Coref: Research implementation (types)

src/eval/
└── coref_resolver.rs   # Coref: Resolvers (Simple, Discourse, Box)
```

## Summary

| Category | Type | Source | Feature Flag | Purpose |
|----------|------|--------|--------------|---------|
| **NER Backends** | Pre-trained | Hugging Face | `onnx`/`candle` | Entity extraction |
| **Box Embeddings** | Research | Our implementation | None | Coreference resolution |

**Key Insight**: NER backends are "off the shelf" models we wrap. Box embeddings are a research implementation we built to encode logical invariants for coreference resolution.

