theiskaa commited on
Commit
5a754d3
·
verified ·
1 Parent(s): 654dcc3

feat: publish the experimental contact model

Browse files
Files changed (5) hide show
  1. NOTICE +9 -0
  2. README.md +166 -0
  3. bundle.json +47 -0
  4. tessera-v1.safetensors +3 -0
  5. tessera-v1.sha256 +1 -0
NOTICE ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ Tessera
2
+
3
+ The code in this repository is licensed under MIT OR Apache-2.0.
4
+
5
+ The trained model weights are licensed under CC BY 4.0. They are derived from the following sources, whose notices are reproduced as their licenses require.
6
+
7
+ OpenStreetMap: Contains information from OpenStreetMap, © OpenStreetMap contributors, available under the Open Database License (https://opendatacommons.org/licenses/odbl/1-0/). The address training data derived from it is itself available under the Open Database License: it is produced deterministically from the libpostal training files published at https://archive.org/download/libpostal-parser-training-data-20170304/ by the trainer's prepare command, using the manifests and label map under data/manifests/.
8
+
9
+ Open Addresses UK: Contains data from Open Addresses UK, published by Open Addresses Ltd under CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/). It includes data originally licensed under the UK Open Government Licence or the Ordnance Survey OpenData Licence: contains public sector information licensed under the Open Government Licence v3.0, and OS data © Crown copyright and database right.
README.md ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ language:
3
+ - en
4
+ license: cc-by-4.0
5
+ pipeline_tag: token-classification
6
+ library_name: tessera
7
+ tags:
8
+ - tessera
9
+ - contact-extraction
10
+ - named-entity-recognition
11
+ - wasm
12
+ - experimental
13
+ ---
14
+
15
+ # tessera
16
+
17
+ A tiny model for extracting contact details from text, locally.
18
+
19
+ <p align="center">
20
+
21
+ [![CI](https://github.com/theiskaa/tessera/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/theiskaa/tessera/actions/workflows/ci.yml)
22
+ [![Model](https://img.shields.io/badge/model-int8%20%C2%B7%203.49%20MB-blue)](#model)
23
+ [![Code license](https://img.shields.io/badge/code-MIT%20OR%20Apache--2.0-blue)](#license)
24
+ [![Weights license](https://img.shields.io/badge/weights-CC%20BY%204.0-blue)](NOTICE)
25
+
26
+ </p>
27
+
28
+ Tessera finds people, organizations, postal addresses, emails, and phone numbers. It returns their exact positions in the original text, splits addresses into components, and groups related details into contacts. The same Rust implementation runs natively and through WebAssembly in browsers, Node, and Bun.
29
+
30
+ Email and phone extraction uses validating rules. Names, organizations, and addresses use an int8 neural network; a separate network parses address components. Inference stays in your process or browser worker. No text is sent to an inference service.
31
+
32
+ **This release is experimental.** The learned detector currently focuses on English United States material. Organization detection and unfamiliar prose need review; fresh unseen release accuracy has not been established. See [results and limitations](#results-and-limitations).
33
+
34
+ [Usage](#usage) · [Model](#model) · [Results](#results-and-limitations) · [License](#license)
35
+
36
+ ## Usage
37
+
38
+ | Operation | Use it to | Returns |
39
+ | -------------------------------------- | -------------------------------------------------------- | -------------------------------------------- |
40
+ | `detect` | Find individual entities in a document | Kind, source offsets, confidence, and origin |
41
+ | `parse_address` / `parseAddress` | Split text already known to be one address | Labeled address components |
42
+ | `extract_contacts` / `extractContacts` | Associate extracted details with people or organizations | Contacts and unassigned entities |
43
+
44
+ Rust and CLI offsets use UTF-8 bytes. JavaScript offsets use UTF-16 code units, so `text.slice(start, end)` returns the entity. Every `end` is exclusive. Keep the original string when using offsets.
45
+
46
+ ### Rust
47
+
48
+ For an application beside a Tessera checkout, add the runtime as a local dependency:
49
+
50
+ ```toml
51
+ [dependencies]
52
+ tessera = { path = "../tessera/tessera" }
53
+ ```
54
+
55
+ Load the bundle and its checksum once, then reuse the extractor:
56
+
57
+ ```rust
58
+ use tessera::{Config, Kind, Query, Tessera};
59
+
60
+ fn main() -> Result<(), Box<dyn std::error::Error>> {
61
+ let bytes = std::fs::read("models/tessera-v1.safetensors")?;
62
+ let checksum = std::fs::read_to_string("models/tessera-v1.sha256")?;
63
+ let extractor = Tessera::load(&bytes, Config {
64
+ kinds: Kind::all(),
65
+ expected_checksum: Some(checksum.trim()),
66
+ })?;
67
+ let query = Query { country_hint: &["US"], ..Query::default() };
68
+ let text = "Jordan Avery, Project Coordinator\nAcme Corporation\n500 Main St, Springfield, IL 62701\n+1 202-555-0199\njordan@acme.example";
69
+ let contacts = extractor.extract_contacts(text, &query)?;
70
+ println!("{} contacts, {} unassigned entities",
71
+ contacts.contacts.len(), contacts.unassigned.len());
72
+ let address = extractor.parse_address("400 Broad St, Seattle, WA 98109", &query)?;
73
+ println!("{} address components", address.components.len());
74
+ Ok(())
75
+ }
76
+ ```
77
+
78
+ `unassigned` is part of a successful extraction: the grouper leaves details there when it cannot confidently choose a contact. Experimental detection may omit or mislabel entities.
79
+
80
+ ### JavaScript
81
+
82
+ Serve the bundle and checksum with your application:
83
+
84
+ ```js
85
+ import { createTessera } from "tessera";
86
+
87
+ const response = await fetch("/models/tessera-v1.sha256");
88
+ if (!response.ok) throw new Error("Could not load model checksum");
89
+ const integrity = (await response.text()).trim();
90
+ const extractor = await createTessera({
91
+ modelUrl: "/models/tessera-v1.safetensors",
92
+ integrity,
93
+ worker: true,
94
+ });
95
+
96
+ try {
97
+ const text = "Contact hello@example.org or +1 202-555-0123.";
98
+ const entities = await extractor.detect(text, { countryHint: ["US"] });
99
+ for (const entity of entities) {
100
+ console.log(entity.kind, text.slice(entity.start, entity.end));
101
+ }
102
+ const contacts = await extractor.extractContacts(text, {
103
+ countryHint: ["US"],
104
+ });
105
+ console.log(contacts.contacts, contacts.unassigned);
106
+ } finally {
107
+ extractor.dispose();
108
+ }
109
+ ```
110
+
111
+ For Node or Bun, read the bundle from disk and pass `modelBytes` instead of `modelUrl`. `worker: true` runs inference off the browser's main thread when workers are available; it is ignored outside that environment. Rules alone need no model fetch: `createTessera({ kinds: ["email", "phone"] })`.
112
+
113
+ The complete JavaScript API is in [index.d.ts](https://github.com/theiskaa/tessera/blob/main/tessera/js/index.d.ts).
114
+
115
+ ### Options
116
+
117
+ - `country_hint` / `countryHint` helps interpret national phone numbers; it does not change the detector's training coverage.
118
+ - `include_uncertain` / `includeUncertain` returns additional uncertain results. Internal detector thresholds still apply.
119
+ - `parse_address` accepts one address of at most 256 non-whitespace tokens and 8 KiB.
120
+ - Rust's optional `markdown` feature scans prose and contact link destinations while retaining source offsets. The default JavaScript package omits it and rejects `format: "markdown"`.
121
+ - Rust failures use `tessera::Error`; JavaScript failures expose a `TesseraError` code. An empty successful result means nothing was returned.
122
+
123
+ ## Model
124
+
125
+ The distributed [Safetensors bundle](tessera-v1.safetensors) contains both the entity detector and address parser. It uses Tessera's own runtime: it is not a Transformers `AutoModel` checkpoint, and no hosted inference endpoint is included.
126
+
127
+ | Property | Value |
128
+ | ------------------------------------ | ----------------------------------------------------- |
129
+ | Model version / bundle format | `0.3.0` / `2` |
130
+ | Runtime version | `0.1.0` |
131
+ | Bundle size | 3,489,616 bytes (3.49 MB; about 3.09 MB with gzip) |
132
+ | Weight encoding | Per-channel symmetric int8; float32 scales and biases |
133
+ | Detector architecture | `detector-context96-rms-v2` |
134
+ | Hidden channels / convolution kernel | 96 / 3 |
135
+ | Detector dilations | `[1, 2, 4, 8, 16, 1, 64]` |
136
+ | Normalization | Channel RMS after each residual block, epsilon `1e-5` |
137
+ | Context radius / window / overlap | 96 / 2,048 / 448 retained tokens |
138
+
139
+ The detector combines hashed character n-grams of lengths 2, 3, and 4 with script, shape, and contextual features. It predicts BIO labels for PERSON, ORG, and ADDRESS. Email and phone spans found by rules are protected from competing model labels. The parser labels components of supplied or detected addresses, and the grouper associates related entities.
140
+
141
+ The Safetensors header contains 39 metadata fields, including graph and decoder identifiers, dimensions, labels, feature settings, licensing, experimental status, and source checkpoint hashes. [bundle.json](bundle.json) exposes the same metadata in readable form. Load [tessera-v1.sha256](tessera-v1.sha256) with the bundle to verify its integrity.
142
+
143
+ Format 2 binds the context graph and decoder explicitly. The runtime also reads legacy format 1 bundles using their original graph. The asset filename is independent of its embedded model version.
144
+
145
+ ## Results and limitations
146
+
147
+ The detector completed 4,000 optimizer updates. It failed the automatic quality-promotion requirements, including the full 95% training-seen gate, and was accepted for an experimental release. The bounded run finished, but the full configured training schedule did not; metadata retains `detector_training_complete=false`.
148
+
149
+ These are **native checkpoint exact entity F1** scores, measured before int8 packaging. Both the kind and boundaries must match:
150
+
151
+ | Evaluation set | Documents | PERSON | ORG | ADDRESS |
152
+ | --------------------------------- | --------: | -----: | -----: | ------: |
153
+ | Real training-seen documents | 1,799 | 96.47% | 75.39% | 97.33% |
154
+ | Reused real development documents | 196 | 76.86% | 53.76% | 89.22% |
155
+
156
+ Training-seen results measure fitting. The development set was reused during model selection. **Neither establishes fresh unseen release accuracy.** These scores do not measure email/phone rules, address parsing, or contact grouping. Aggregate PERSON results also conceal weaker biography coverage; organization names, aliases, and boundaries remain a substantial source of error.
157
+
158
+ Separately, the **packaged address parser** scored 99.05% component F1 and 95.23% completely correct parses on its 3,000-row US test shard through `parse_address`. This tests parsing supplied addresses, not detecting them in prose.
159
+
160
+ Eight of thirteen small public detector fixture cases remain documented failures, including missed people in prose and tables. Their expected labels are retained. These examples expose limitations; they are not an independent accuracy benchmark.
161
+
162
+ Confidence and `review_recommended` are signals for inspection, not guarantees. Check names, organizations, and contact assignments before using them in important records. Other countries, languages, and unfamiliar document layouts have no demonstrated general accuracy from this evaluation.
163
+
164
+ ## License
165
+
166
+ Code is available under [MIT](https://github.com/theiskaa/tessera/blob/main/LICENSE-MIT) or [Apache-2.0](https://github.com/theiskaa/tessera/blob/main/LICENSE-APACHE). Trained weights are licensed under CC BY 4.0; retain [NOTICE](NOTICE) when redistributing them. It preserves the underlying source attributions, including OpenStreetMap and Open Addresses UK. Training combines synthetic contacts and annotated real US documents; the parser has its own training history. Raw corpora and local run evidence are not part of the model upload.
bundle.json ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "runtime": "tessera",
3
+ "file": "tessera-v1.safetensors",
4
+ "bytes": 3489616,
5
+ "sha256": "abfa972301f82d23240cc15bc9f70c7c574bb047e33473254a737abacfe995ff",
6
+ "metadata": {
7
+ "created": "2026-10-02",
8
+ "decoder_contract": "tessera-bio-context96-address-continuation-v1",
9
+ "description": "Experimental contact extraction for United States documents",
10
+ "detector_architecture": "{\"name\":\"detector-context96-rms-v2\",\"dilations\":[1,2,4,8,16,1,64],\"kernel\":3,\"channels\":96,\"operator\":\"post-residual-channel-rms-v2\",\"epsilon\":0.00001,\"learned_parameters\":0,\"margin_tokens\":96,\"overlap_tokens\":448,\"window_tokens\":2048,\"max_entity_tokens\":256,\"decoder_contract\":\"tessera-bio-context96-address-continuation-v1\"}",
11
+ "detector_best_sha256": "f4248d1cd0b9f208f4143cacd284c6009352385d0199799e9eac42ff3e35a2aa",
12
+ "detector_input_snapshot_sha256": "ece44fc87959a391b3f99f3c113655f26cf8e45d2c7bc44f474b10bfc1b6517c",
13
+ "detector_labels": "[\"O\",\"B-PERSON\",\"I-PERSON\",\"B-ORG\",\"I-ORG\",\"B-ADDRESS\",\"I-ADDRESS\"]",
14
+ "detector_network": "{\"hidden\":96,\"kernel\":3,\"dilations\":[1,2,4,8,16,1,64]}",
15
+ "detector_parameter_sha256": "b9ca96cf3b9cd92d96151e6226eb134a04312d691a4dd603fbe8076625cc0b87",
16
+ "detector_quantized_sha256": "2d211666c7e04fceb61073fdbbd73d5457019feed7ed5f437c3a0f3b94eddaa2",
17
+ "detector_training_complete": "false",
18
+ "detector_training_updates": "4000",
19
+ "evaluation_scope": "training-seen and reused development; no fresh unseen evaluation",
20
+ "experimental_regions": "[\"US\"]",
21
+ "feature_config": "{\"ngram_sizes\":[2,3,4],\"hash_buckets\":32768,\"hash_seed\":0,\"max_ngrams_per_token\":64,\"flag_bits\":23,\"script_rows\":13,\"shape_rows\":64}",
22
+ "format": "2",
23
+ "fresh_unseen_evaluation_pending": "true",
24
+ "general_accuracy_claim": "false",
25
+ "license": "CC-BY-4.0",
26
+ "model_name": "Tessera",
27
+ "model_scope": "US",
28
+ "model_version": "0.3.0",
29
+ "nets": "parser,detector",
30
+ "parser_best_sha256": "9c5d0ec78099d58d7ffc9d1b20ca3c4a7e31cbc5cf598afb63a9d11bf48a73ba",
31
+ "parser_input_snapshot_sha256": "46d8b63046534c904b233f5090d3d4cc0b0d1ddd403d4621858bb453c991ba48",
32
+ "parser_labels": "[\"O\",\"B-house_number\",\"I-house_number\",\"B-road\",\"I-road\",\"B-unit\",\"I-unit\",\"B-level\",\"I-level\",\"B-suburb\",\"I-suburb\",\"B-city\",\"I-city\",\"B-district\",\"I-district\",\"B-region\",\"I-region\",\"B-postcode\",\"I-postcode\",\"B-country\",\"I-country\",\"B-po_box\",\"I-po_box\"]",
33
+ "parser_network": "{\"hidden\":96,\"kernel\":3,\"dilations\":[1,2,4,8]}",
34
+ "parser_quantized_sha256": "ffb631258693b17cfb12a23b8bb0372512dbdaa04b1bdfbde1703e0e6da30576",
35
+ "phone_metadata_version": "libphonenumber v9.0.33 generated tables",
36
+ "quantization": "per-channel symmetric int8",
37
+ "release_approval_sha256": "a1fe01d61bf3be31ec20d5aef05af01c9d0b9be7d0f7002adcb6cd2fbcf13bf0",
38
+ "report_url": "",
39
+ "repository": "https://github.com/theiskaa/tessera",
40
+ "runtime_version": "0.1.0",
41
+ "status": "experimental",
42
+ "supported_regions": "[]",
43
+ "tokenizer_contract": "tessera-tokenize-legacy-v1",
44
+ "training_seen_95_percent_gate_passed": "false",
45
+ "training_snapshot": "parser-inputs:46d8b63046534c904b233f5090d3d4cc0b0d1ddd403d4621858bb453c991ba48,detector-inputs:ece44fc87959a391b3f99f3c113655f26cf8e45d2c7bc44f474b10bfc1b6517c"
46
+ }
47
+ }
tessera-v1.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:abfa972301f82d23240cc15bc9f70c7c574bb047e33473254a737abacfe995ff
3
+ size 3489616
tessera-v1.sha256 ADDED
@@ -0,0 +1 @@
 
 
1
+ sha256-abfa972301f82d23240cc15bc9f70c7c574bb047e33473254a737abacfe995ff