Library API
Scope
This page describes the five entry points a consumer calls from TypeScript: createRuntimePipeline,
NeuralAddressClassifier, the geocodinggeocodingThe process of converting an address into geographic coordinates (latitude and longitude). Mailwoman geocodes in a multi-tier cascade: exact address-point match → street interpolation → locality centroid. Each tier is progressively coarser but more widely available. functions in mailwoman/geocode-core, the batchbatch sizeHow many examples the model processes before each parameter update. Larger batches give smoother gradients but cost more memory; gradient accumulation simulates a big batch on a small GPU. iterator in
mailwoman/geocode-stream, and createCalibrator. Each section carries a full runnable file and the
output that file produced.
It does not describe the stagestageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). packages (@mailwoman/normalize, @mailwoman/query-shape,
@mailwoman/locale-gate, @mailwoman/kind-classifier, @mailwoman/phrase-grouper). Those are wired
by createRuntimePipeline with production defaults, and a consumer who calls them directly is
building a pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package. rather than using one. For the command line, see CLI. For HTTP, see
HTTP APIs.
Every example below ran under Node 26 against the packages in this repository at the version this page documents. The options tables are a subset: bias-magnitude and gate-threshold knobs that exist to be swept during evaluationevalRunning the model against a held-out golden dataset and computing per-component F1, exact-match, calibration, and resolved-coordinate error. are omitted, and their defaults are the shipped behavior. Where a table is a subset, it says so and says what it left out.
Import map
mailwoman exports eight import paths, one of which takes a wildcard suffix. On Node the package
serves TypeScript source directly, so no build step stands between an installed package and a running
script.
| Import | What it carries |
|---|---|
mailwoman | createRuntimePipeline, plus everything @mailwoman/core re-exports |
mailwoman/geocode-core | geocodeAddress, parseForGeocode, ShardProvider, the result types |
mailwoman/geocode-stream | geocodeStream |
mailwoman/resolver-backend | createResolverBackend, resolveCandidateDBPath, data-root path helpers |
mailwoman/cli-kit | Ink and Pastel helpers for building a command |
mailwoman/test-kit | The parser test helpers |
mailwoman/poi-overpass | OverpassQL export for a matched POIpoint of interest (POI). A named place that is not strictly an address — landmark, transit stop, venue, amenity, or franchise. Mailwoman tags these as venue and resolves them through the gazetteer. intent |
mailwoman/gazetteer-pipeline | GazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. build internals, plus a wildcard subpath for its modules |
geocodeAddress and its result types are reachable only through mailwoman/geocode-core. They are
not re-exported from the bare specifier.
createRuntimePipeline
function createRuntimePipeline(
opts?: CreateRuntimePipelineOpts
): (raw: string, runOpts?: PipelineOpts) => Promise<PipelineResult>
The factory is synchronous and the returned function is async. Every lazy load — the coarse countrycountryThe top-level address component (an ISO country). Closed-vocabulary, so it is best handled by a deterministic matcher feeding a proposal rather than a retrained model head. placer, the gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. index, the streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels.-morphology matcher, the streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels.-evidence index — happens on the first call to the returned function, not in the factory.
Options
Two stagesstageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). take an injected implementation. The rest carry production defaults.
| Option | Type | Default | Effect |
|---|---|---|---|
classifier | AddressClassifier | none | StagestageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). 3. Without it the pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package. runs the structural stagesstageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). only |
resolver | Resolver | none | StagestageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). 6. Without it the tree carries no coordinates |
fst | FSTMatcher or false | auto-load | GazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. emission priorsemission priorA log-probability bias injected into the model's emission logits from an external signal — gazetteer frequency, an FST, a Wikipedia-importance score — combined with the learned weights at decode time.. Loaded from the classifier's weightsparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. package |
streetMorphology | FSTMatcher or false | auto-load | Signal source for the street-context gatestreet-context gateThe decode-time seam that zeroes locality evidence on inputs with no street-painter hits — bare lookups must not have city names painted into street structure. Every measured evidence win class carries street words, so the gate costs nothing. |
streetEvidence | StreetLocalityEvidence, false | auto-load | Reranks the streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. on atlas-confirmed evidence. Requires a spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree.-headattention headOne of several parallel attention computations in a layer, each free to focus on a different kind of relationship between tokens. Their outputs are concatenated — 'multi-head attention'. Mailwoman uses 4 heads. modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' |
placeCountry | function or false | the bundled coarse placer | A confident countrycountryThe top-level address component (an ISO country). Closed-vocabulary, so it is best handled by a deterministic matcher feeding a proposal rather than a retrained model head. guess becomes a soft ranking prior. It never filters |
poiQueryKind | boolean or { poiDatabasePath } | true (intent only) | Detects a POIpoint of interest (POI). A named place that is not strictly an address — landmark, transit stop, venue, amenity, or franchise. Mailwoman tags these as venue and resolves them through the gazetteer. query and extracts its intent. The object form also executes it |
normalizeCase | boolean | on | Title-cases detected all-caps ASCII input before the modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' |
hardPlaceCountry | boolean | on | Promotes a confident countrycountryThe top-level address component (an ISO country). Closed-vocabulary, so it is best handled by a deterministic matcher feeding a proposal rather than a retrained model head. guess to a filter, inside a coveragecoverageThe fraction of a population or region for which a data source has real, non-placeholder entries — e.g. 47% rooftop coverage on Texas addresses. Distinct from accuracy on the rows that are present. safelist |
hardCountrySafelist | ReadonlySet<string> | the gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture.'s own manifest | Which countries the hard filter applies to. Falls back to a built-in list |
detectLocale | function | @mailwoman/locale-gate | StagestageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). 2 |
classifyKind | function | @mailwoman/kind-classifier | StagestageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). 2.5. An explicit override wins over the POIpoint of interest (POI). A named place that is not strictly an address — landmark, transit stop, venue, amenity, or franchise. Mailwoman tags these as venue and resolves them through the gazetteer.-aware default |
groupPhrases | function | @mailwoman/phrase-grouper | StagestageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). 2.7 |
Passing false to fst, streetMorphology, streetEvidence, placeCountry or poiQueryKind
suppresses both the auto-load and any explicit value, which is how a caller gets output identical to a
build without that mechanism.
Per-call options
The second argument to the returned function is PipelineOpts. A per-call value overrides the
factory default.
| Field | Type | Default | Effect |
|---|---|---|---|
locale | string | derived by the locale gatelocale gateStage 2 of the runtime pipeline: rule-based locale detection from the query shape's script and known-format signals. Returns a LocaleHint with the top candidate and alternatives, surfacing disagreement with an explicit --locale flag. | BCP-47 tag, for example en-US |
inputMode | "fragmented" or "formatted" | derived from the kind | Which registerinput modeThe Decision-A register switch: 'fragmented' (human-typed fragments — feeds the evidence channels) vs 'formatted' (complete records — runs the trained absence identity). Explicit on CLI/API; per-endpoint defaults (batch→formatted, autocomplete→fragmented); kind-derived otherwise. the evidence channelsevidence channelA dedicated model input that injects externally computed per-token features (confidence-scaled, own projection) at the embedding layer: postcode anchor, gazetteer, country, street-type, locality-surface. The clue informs; the model decides (model-first). run in |
resolveOpts | ResolveOpts | none | Passed through to the resolverresolverThe component that converts parsed address components (locality, region, postcode) into coordinates by looking them up in the gazetteer. The resolver ranks candidates by name match, population, and proximity, and returns the best-matching place with its centroid or polygon. |
normalizeCase | boolean | the factory value | Overrides the factory default for this call |
hardPlaceCountry | boolean | the factory value | Overrides the factory default for this call |
forceFullPipeline | boolean | off | Disables the structural fast paths. A debugging aid |
jointReconcile | boolean | off | Retired as a default in 2026-06. Kept for comparison runs |
signal | AbortSignal | none | Cancels the call |
Return shape
| Field | Type | Meaning |
|---|---|---|
input | string | The raw string as passed |
normalized | NormalizedInputLite | StagestageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). 1 output |
queryShape | QueryShapeLite | Character classcharacter classA token's character category — digit, alpha, CJK, Cyrillic, Arabic, mixed — used by the query-shape stage as a structural signal for locale and kind inference., segmentation, known-format hits |
locale | LocaleHint | Tag, confidence, alternatives, and whether it was detected or given |
kind | QueryKindResult | The query kindquery kindThe coarse category the kind classifier assigns to the whole input — postcode_only, locality_only, structured_address, intersection, po_box, landmark, or vague — used to route processing., for example structured_address |
phraseProposals | PhraseProposal[] | StagestageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). 2.7 proposals. Empty when a fast path skipped the stagestageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). |
tree | AddressTree | The parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. |
poiIntent | POIIntentOutcome or absent | Present only when path is "poi" |
timing | Record<string, number> | Milliseconds per stagestageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). |
path | "fast-path", "full", or "poi" | Which route the input took |
Example
import { decodeAsJSON } from "@mailwoman/core/decoder"
import { NeuralAddressClassifier } from "@mailwoman/neural"
import { createRuntimePipeline } from "mailwoman"
const classifier = await NeuralAddressClassifier.loadFromWeights({ locale: "en-US" })
const pipeline = createRuntimePipeline({ classifier })
const result = await pipeline("350 5th Ave, New York, NY 10118")
console.log(JSON.stringify(decodeAsJSON(result.tree), null, 2))
console.log(result.kind.kind, "|", result.path, "|", result.locale.locale)
Output:
{
"region": "NY",
"locality": "New York",
"street": "5th",
"house_number": "350",
"street_suffix": "Ave",
"postcode": "10118"
}
structured_address | full | en-US
decodeAsJSON flattens the tree to a tag-to-value object and drops the containment nesting. Read
result.tree directly when the nesting matters.
NeuralAddressClassifier
Reach for the classifier directly in three cases: you need the labelcomponent tagOne of the 25 labels in Mailwoman's address schema — street, locality, region, postcode, house_number, unit, po_box, country, venue, intersection, and others. Each parsed span carries exactly one component tag. distribution rather than the
argmax tree, you are serving several localeslocaleThe combination of language and country an address comes from. en-US and fr-FR are the locales Mailwoman ships weights for. from one process and want to hold each modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' open, or
you are running the modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' in an environment createRuntimePipeline does not fit. For a single parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates.,
the pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package. is the shorter path and it applies the structural stagesstageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). the modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' was trained beside.
static async loadFromWeights(opts?: {
locale?: string
modelPath?: string
tokenizerPath?: string
modelCardPath?: string
tier?: "server" | "pocket"
cacheRoot?: string
postcodeAnchorLookup?: AnchorLookup
executionProviders?: string[]
intraOpNumThreads?: number
}): Promise<NeuralAddressClassifier>
Resolution runs in one order: an explicit cacheRoot holding both binaries, then explicit paths in
opts, then the installed @mailwoman/neural-weights-<locale> package, then the user-level weightsparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values.
cache that mailwoman parse --download-weights writes, then a single actionable error naming all of
them. A package that resolves but ships no binaries fails loudly rather than falling through to the
cache — that is the metadata-only checkout trap, and silence there would look like a working install.
The method is Node-only: it imports onnxruntime-node and node:fs dynamically. In a browser, use
loadNeuralClassifierFromURLs from @mailwoman/neural/web-loader. Both runtimes are optional peer
dependencies, so whichever side you are on, install it yourself: onnxruntime-node on a server,
onnxruntime-web in a browser build.
intraOpNumThreads defaults to 4, which is a cap rather than a core count. Measured over 120 parsesaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates.
on 2026-08-03: one threadthreadA parallel workstream within a release. Threads compose; they are not sequential milestones like phases. costs 18.3 ms per parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates., two cost 12.5 ms, four cost 9.2 ms, and all cores
cost 9.3 ms — so the cap is free. Pin it to 1 only when you are running several classifier instances
in one process and want to bound total threadthreadA parallel workstream within a release. Threads compose; they are not sequential milestones like phases. count, and expect twice the latency.
One classifier instance holds one localelocaleThe combination of language and country an address comes from. en-US and fr-FR are the locales Mailwoman ships weights for.. Construct one per localelocaleThe combination of language and country an address comes from. en-US and fr-FR are the locales Mailwoman ships weights for. you serve.
| Method | Returns | Use it for |
|---|---|---|
parse(text, opts?) | Promise<AddressTree> | The nested tree |
parseJSON(text, opts?) | Promise<Partial<Record<ComponentTag, string>>> | A flat tag-to-value object |
parseTuples(text, opts?) | Promise<Array<[ComponentTag, string]>> | Ordered tag/value pairs |
parseXML(text, opts?) | Promise<string> | An XML projection |
parseWithLogits(text, opts?) | Promise<ParseWithLogitsResult> | The tree plus per-tokentokenOne word or subword in the tokenized input. For the neural classifier, tokens come from SentencePiece (subword units); for the rule classifiers, tokens are whitespace- and punctuation-separated words. logitslogitA raw, unnormalized per-label score the model outputs before softmax. Priors and biases are added in logit space, then softmax turns logits into probabilities. and piece offsets |
traceParse(text, opts?) | Promise<NeuralParseTrace> | StagestageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone).-by-stagestageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). diagnostics |
Three readonly properties surface paths and grammar the caller's layerlayerOne transformer block — attention plus a feed-forward network, with normalization and residual connections — applied to every position. Stacking layers lets the model build up richer representations; Mailwoman's encoder has 6. consumes: fstPath,
streetMorphologyPath, and spanGrammar. @mailwoman/neural deliberately carries no resolverresolverThe component that converts parsed address components (locality, region, postcode) into coordinates by looking them up in the gazetteer. The resolver ranks candidates by name match, population, and proximity, and returns the best-matching place with its centroid or polygon.
dependency, so it reports where the gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. index is and leaves deserialization to the caller.
createRuntimePipeline reads all three.
Parse options
These are the fields of ParseOpts a consumer sets. ParseOpts has 20 fields; the ten omitted here
are the FSTFST (finite-state transducer). A compact automaton that reads an input sequence and emits an output sequence. Mailwoman encodes gazetteer names and street affixes as FSTs for fast prefix matching and prior injection without search overhead. bias magnitudes and gate thresholds (fstBiasScale, fstStreetContextGate and their
siblings, four of which the source marks internal), the queryShapeBiasScale magnitude, the
morphology-prior override, the pre-v4.4.0 bridgePunctuationGaps switch, and trailingLocality,
which is deprecated and scheduled for deletion at the next major.
| Field | Type | Default | Effect |
|---|---|---|---|
fst | FSTMatcherLike | none | GazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. matches become additive emission biasesadditive biasA logit adjustment added before softmax from a prior or external knowledge source, typically soft-capped so it influences but never overrides the model's prediction. The mechanism behind shallow fusion. |
inputMode | "fragmented" or "formatted" | "fragmented" | Which registerinput modeThe Decision-A register switch: 'fragmented' (human-typed fragments — feeds the evidence channels) vs 'formatted' (complete records — runs the trained absence identity). Explicit on CLI/API; per-endpoint defaults (batch→formatted, autocomplete→fragmented); kind-derived otherwise. the evidence channelsevidence channelA dedicated model input that injects externally computed per-token features (confidence-scaled, own projection) at the embedding layer: postcode anchor, gazetteer, country, street-type, locality-surface. The clue informs; the model decides (model-first). run in |
queryShape | QueryShapeLike | none | Known-format hits become additive emission biasesadditive biasA logit adjustment added before softmax from a prior or external knowledge source, typically soft-capped so it influences but never overrides the model's prediction. The mechanism behind shallow fusion. |
normalizeCase | boolean | on | Title-cases detected all-caps ASCII input |
calibrate | Calibrator | off | Maps raw spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. confidence through a calibration table |
enforceWordConsistency | boolean or WordConsistencyOpts | on | Per-word BIO vote, skipping byte-fallback words |
postcodeRepair | boolean | off | Deterministic postcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one. repair pass |
unitRepair | boolean | off | Deterministic unitunitA subdivision of a building — apartment, suite, floor — that refines a street address. Mailwoman's unit component; a designator plus identifier forms a subpremise. repair pass |
spanProposer | boolean | on | SpanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree.-proposal priors |
addressSystemConventions | "auto" or a system code | from the model cardmodel cardA JSON metadata file (model-card.json) shipped with each weights bundle. It declares the model version, lineage, label set, required inference channels (anchor, gazetteer), calibration data, and training provenance. | Which address-system conventions apply |
placetypePair | PlacetypePairPriorOpts or false | the weightsparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. package's pair index | Omitting it inherits the package. Only false disables it |
The table above is the classifier's own defaults, and createRuntimePipeline pins two of them on
every call it makes: postcodeRepair: true, and the shipped word-consistency settings.
enforceWordConsistency is on either way — the pin only removes the caller's ability to change it
mid-pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package.. postcodeRepair is the one that differs: off on a bare classifier call, on
through the pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package..
Wiring the gazetteer index on a bare call
fst is the option a bare classifier call has to pass, and the one whose absence is hardest to
notice. The gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. index turns a gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. match into an additive emission biasadditive biasA logit adjustment added before softmax from a prior or external knowledge source, typically soft-capped so it influences but never overrides the model's prediction. The mechanism behind shallow fusion., and it is what
tells the modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' that a bare tokentokenOne word or subword in the tokenized input. For the neural classifier, tokens come from SentencePiece (subword units); for the rule classifiers, tokens are whitespace- and punctuation-separated words. naming a real place is a localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighborhood in the hierarchy. rather than a streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels.. Nothing
loads it for you outside createRuntimePipeline: the pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package. reads the index out of the
classifier's weightsparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. package on its first call and passes it to every parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates., while a classifier you
call directly receives no index at all. geocodeAddress does not wire it either, and GeocodeDeps
has no field for it.
The parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. still succeeds without it. It is a prior, not a stagestageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone)., so most inputs are unaffected and the failure is silent — which is why this section exists rather than a note on the table row.
Wire it from the path the classifier already resolved. fstPath is optional, because a localelocaleThe combination of language and country an address comes from. en-US and fr-FR are the locales Mailwoman ships weights for.
overlay that ships no gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. index leaves it undefined, so guard it rather than reading it
straight into readFileSync.
import { readFileSync } from "node:fs"
import { NeuralAddressClassifier } from "@mailwoman/neural"
import { deserializeFST } from "@mailwoman/resolver-wof-sqlite/fst-serialize"
const classifier = await NeuralAddressClassifier.loadFromWeights({ locale: "en-US" })
// `fstPath` is optional — a locale overlay that ships no gazetteer index leaves it undefined.
const fst = classifier.fstPath ? deserializeFST(readFileSync(classifier.fstPath)) : undefined
for (const input of ["Hollywood", "big spring"]) {
console.log(input)
console.log(" without fst:", JSON.stringify(await classifier.parseJSON(input)))
console.log(" with fst: ", JSON.stringify(await classifier.parseJSON(input, { fst })))
}
Output:
Hollywood
without fst: {"street":"Hollywood"}
with fst: {"locality":"Hollywood"}
big spring
without fst: {"street":"Big","street_suffix":"Spring"}
with fst: {"locality":"Big Spring"}
resolveWeights from @mailwoman/neural/weights returns the same fstPath without constructing a
classifier. It is synchronous, so do not await it, and its fstPath is optional for the same reason.
This is the main reason to prefer the pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package. for a single parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates.: it wires the index, the streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels.-morphology gate and the streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels.-evidence rerank, and a bare classifier call gets none of the three.
Geocoding
mailwoman/geocode-core turns a string into a coordinate with a resolution tier and an uncertainty
radius.
function geocodeAddress(input: string, deps: GeocodeDeps): Promise<GeocodeResult>
geocodeAddress always returns a result. With no coordinate shardsshardA partial output file of the corpus build, written in Parquet format. The training pipeline streams shards row by row. wired it returns the admin tier
rather than failing. It throws only on a fatal parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. or resolve error, so batchbatch sizeHow many examples the model processes before each parameter update. Larger batches give smoother gradients but cost more memory; gradient accumulation simulates a big batch on a small GPU. callers catch per
row.
Dependencies
GeocodeDeps has 18 fields. The 16 below are the ones a consumer sets; osmShards and
hardCountrySafelist are omitted as evaluationevalRunning the model against a held-out golden dataset and computing per-component F1, exact-match, calibration, and resolved-coordinate error. and build-time wiring.
| Field | Type | Default | Effect |
|---|---|---|---|
classifier | GeocodeClassifier | required | The parser |
inputMode | "fragmented" / "formatted" | from the query kindquery kindThe coarse category the kind classifier assigns to the whole input — postcode_only, locality_only, structured_address, intersection, po_box, landmark, or vague — used to route processing. | Which registerinput modeThe Decision-A register switch: 'fragmented' (human-typed fragments — feeds the evidence channels) vs 'formatted' (complete records — runs the trained absence identity). Explicit on CLI/API; per-endpoint defaults (batch→formatted, autocomplete→fragmented); kind-derived otherwise. the evidence channelsevidence channelA dedicated model input that injects externally computed per-token features (confidence-scaled, own projection) at the embedding layer: postcode anchor, gazetteer, country, street-type, locality-surface. The clue informs; the model decides (model-first). run in |
resolver | Resolver | required | The gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. walk |
shards | ShardResolver | none | Per-state rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few meters — the highest tier of the geocode cascade. Sourced from address-point and situs data. and interpolationinterpolationA geocoding technique that estimates a coordinate along a street segment based on the house number range. Used as the middle tier of Mailwoman's geocode cascade when exact address-point data is unavailable. lookups |
nationalShards | (country) => StateShards | none | Per-countrycountryThe top-level address component (an ISO country). Closed-vocabulary, so it is best handled by a deterministic matcher feeding a proposal rather than a retrained model head. shardsshardA partial output file of the corpus build, written in Parquet format. The training pipeline streams shards row by row., for example the French rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few meters — the highest tier of the geocode cascade. Sourced from address-point and situs data. shardshardA partial output file of the corpus build, written in Parquet format. The training pipeline streams shards row by row. |
defaultCountry | string | none | Scopes the resolverresolverThe component that converts parsed address components (locality, region, postcode) into coordinates by looking them up in the gazetteer. The resolver ranks candidates by name match, population, and proximity, and returns the best-matching place with its centroid or polygon. to one countrycountryThe top-level address component (an ISO country). Closed-vocabulary, so it is best handled by a deterministic matcher feeding a proposal rather than a retrained model head. |
bias | proximity points | none | Soft re-rank toward a viewport or a user location |
interpCalibration | number or a table | the shardshardA partial output file of the corpus build, written in Parquet format. The training pipeline streams shards row by row.'s own value | Multiplier on the interpolationinterpolationA geocoding technique that estimates a coordinate along a street segment based on the house number range. Used as the middle tier of Mailwoman's geocode cascade when exact address-point data is unavailable. tier's uncertainty radius |
normalizeCase | boolean | on | Title-cases detected all-caps input |
normalizeInput | boolean | on | Runs the normalizenormalizeStage 1 of the runtime pipeline: deterministic input preprocessing (Unicode NFC, punctuation normalization, whitespace collapse). Returns a NormalizedInput with an offsetMap that maps normalized positions back to the raw input. stagestageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). |
placeCountry | function or false | the bundled placer | Soft countrycountryThe top-level address component (an ISO country). Closed-vocabulary, so it is best handled by a deterministic matcher feeding a proposal rather than a retrained model head. prior |
hardPlaceCountry | boolean | on | Hard countrycountryThe top-level address component (an ISO country). Closed-vocabulary, so it is best handled by a deterministic matcher feeding a proposal rather than a retrained model head. filter inside the coveragecoverageThe fraction of a population or region for which a data source has real, non-placeholder entries — e.g. 47% rooftop coverage on Texas addresses. Distinct from accuracy on the rows that are present. safelist |
adminCoherence | boolean | on | Joint admin-consistency re-pick |
postcodeCountryPrior | boolean | on | PostcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one. shape informs the countrycountryThe top-level address component (an ISO country). Closed-vocabulary, so it is best handled by a deterministic matcher feeding a proposal rather than a retrained model head. |
retryAlternateRegister | boolean | on | Retries the parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. in the other input registerinput modeThe Decision-A register switch: 'fragmented' (human-typed fragments — feeds the evidence channels) vs 'formatted' (complete records — runs the trained absence identity). Explicit on CLI/API; per-endpoint defaults (batch→formatted, autocomplete→fragmented); kind-derived otherwise. on a weak result |
parsedTree | AddressTree | none | Skips the parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. when you already have the tree |
Resolution tiers
The tier states which artifact produced the coordinate, best first. Gate any house-grade rendering on
the tier, not on the presence of house_number: the parsed spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. is populated whatever tier answered.
| Tier | Coordinate source | uncertainty_m |
|---|---|---|
address_point | RooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few meters — the highest tier of the geocode cascade. Sourced from address-point and situs data. or parcelparcelA property polygon or record carrying a situs (site) address and often a separate owner mailing address. County GIS parcel aggregations are a training source for address-point variety and situs-vs-owner divergence. centroid | A small floor, about 1 m |
interpolated | House-number estimate along a streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. | The calibrated bracket spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. |
street | StreetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. centroid for a streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. query | Half the streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. bounding-box diagonal |
admin | Admin centroid from the gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. | null — no sub-localitydependent localityA sub-locality (neighborhood or borough) hierarchically inside a larger locality — e.g. Brooklyn within New York City. Provides finer geographic specification below the primary locality. estimate exists |
Result
| Field | Type | Meaning |
|---|---|---|
input | string | The raw string as passed |
lat, lon | number or null | The resolved coordinate |
resolution_tier | ResolutionTier | Which tier produced it |
uncertainty_m | number or null | Radius in meters. null on the admin tier |
locality | string or null | Parsed localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighborhood in the hierarchy. spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. |
region | string or null | Parsed regionregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality. spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. |
postcode | string or null | Parsed postcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one. spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. |
house_number | string or null | Parsed house numberhouse numberThe numeric or alphanumeric identifier of a building on a street. Mailwoman's house_number component; its position relative to the street name flips between locales., whatever the tier |
street | string or null | Parsed streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels., reassembled from prefix, base and suffix |
venue | string or null | Parsed venuevenueA named, non-address place — a business, building, park, or stadium. Mailwoman's free-text point-of-interest component, added as a Tier 2 fine label. spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. |
dependent_locality | string or null | Parsed sub-localitydependent localityA sub-locality (neighborhood or borough) hierarchically inside a larger locality — e.g. Brooklyn within New York City. Provides finer geographic specification below the primary locality. spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. |
countryCode | string or null | ISO 3166 alpha-2 of the deepest resolved node |
hierarchy | array, most specific first | The resolved admin chain, each with a name and a gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. place ID |
candidates | array, winner first | Ranked alternatives for the primary place |
hierarchy is the resolved view and carries only nodes the resolverresolverThe component that converts parsed address components (locality, region, postcode) into coordinates by looking them up in the gazetteer. The resolver ranks candidates by name match, population, and proximity, and returns the best-matching place with its centroid or polygon. decorated. The flat spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. fields
are the parsed view. The two disagree whenever the parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. found something the gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. did not.
Shards
ShardProvider opens per-state rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few meters — the highest tier of the geocode cascade. Sourced from address-point and situs data. and interpolationinterpolationA geocoding technique that estimates a coordinate along a street segment based on the house number range. Used as the middle tier of Mailwoman's geocode cascade when exact address-point data is unavailable. databases on demand, caches the handles, and
swaps them atomically when the data release changes.
class ShardProvider {
constructor(factory: ShardLookupFactory, dataRoot: string)
readonly for: ShardResolver
versions(): DataReleaseManifest | null
reload(): DataReleaseManifest | null
close(): void
}
The constructor is positional. factory is the @mailwoman/resolver-wof-sqlite module namespace in
practice. for is a property rather than a method so it can be passed unbound as deps.shards.
Example
This file resolves against a downloaded candidate.db with no streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. shardsshardA partial output file of the corpus build, written in Parquet format. The training pipeline streams shards row by row. wired, so it answers on
the admin tier. It also runs without the gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. emission priorsemission priorA log-probability bias injected into the model's emission logits from an external signal — gazetteer frequency, an FST, a Wikipedia-importance score — combined with the learned weights at decode time.: geocodeAddress never wires the
index, and GeocodeDeps has no field for it — see Wiring the gazetteer index on a bare
call.
import { NeuralAddressClassifier } from "@mailwoman/neural"
import { createWOFResolver } from "@mailwoman/resolver"
import { WOFCandidateTableLookup } from "@mailwoman/resolver-wof-sqlite"
import { geocodeAddress } from "mailwoman/geocode-core"
const classifier = await NeuralAddressClassifier.loadFromWeights({ locale: "en-US" })
// Replace <CANDIDATE_DB> with the path `mailwoman data pull candidate` printed.
const lookup = new WOFCandidateTableLookup({ databasePath: "<CANDIDATE_DB>" })
const resolver = createWOFResolver(lookup)
const result = await geocodeAddress("350 5th Ave, New York, NY 10118", { classifier, resolver })
console.log(JSON.stringify(result, null, 2))
lookup.close()
Output:
{
"input": "350 5th Ave, New York, NY 10118",
"lat": 40.694457,
"lon": -73.93045,
"resolution_tier": "admin",
"uncertainty_m": null,
"locality": "New York",
"region": "NY",
"postcode": "10118",
"house_number": "350",
"street": "5th Ave",
"venue": null,
"dependent_locality": null,
"countryCode": "US",
"hierarchy": [
{
"tag": "locality",
"value": "New York",
"name": "New York",
"lat": 40.694457,
"lon": -73.93045,
"placeID": "wof:85977539"
},
{
"tag": "region",
"value": "NY",
"name": "New York",
"lat": 42.921227,
"lon": -75.596537,
"placeID": "wof:85688543"
}
],
"candidates": [
{
"name": "New York",
"tag": "locality",
"lat": 40.694457,
"lon": -73.93045,
"countryCode": "US",
"placeID": "wof:85977539"
}
]
}
Batch
function geocodeStream(
records: AsyncIterable<SourceRecord> | Iterable<SourceRecord>,
opts: GeocodeStreamOptions
): AsyncIterableIterator<SourceRecord>
geocodeStream runs a worker pool over a record stream and yields enriched records in completion
order, not input order. It parsesaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. once per row and shares the tree between the returned components
and the geocode.
| Option | Type | Default | Effect |
|---|---|---|---|
mapping | ColumnMapping | required | Which columns hold the address |
geocode | GeocodeStreamConfig | required | wofDBPath, dataRoot, locale, country |
concurrency | number | Math.min(4, availableParallelism()) | Worker count |
batchSize | number | 32 | Records handed to a worker at a time |
worker | string or URL | the bundled worker | A replacement worker entry point |
One constraint decides whether this function fits your setup. Its worker constructs a
WOFSqlitePlaceLookup directly, so it resolves against a full Who's On FirstWOF (Who's On First). An open-source gazetteer of places maintained by Mapzen/whosonfirst. Mailwoman builds a custom SQLite database from WOF GeoJSON repos, extended with postcode data, importance scores, and coincident-role relations. SQLite distribution and
cannot use a candidate.db. If your setup is the candidate.db one the tutorials teach, run your own
pool over geocodeAddress instead — Batch geocode at
volume has that loop.
Concurrency is low on purpose. A measured sweep over a single 4 GB database on a 16-core host peaked at two workers, about 1.4 times baseline, and degraded above that.
Confidence
SpanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. confidences from the shipped modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' are under-confident. createCalibrator builds the mapping
that corrects them.
function createCalibrator(table: CalibrationTable | CalibrationBin[]): Calibrator
type Calibrator = (rawConfidence: number) => number
The returned function clamps its input to the range 0 to 1, mapping a non-numeric input to 0. Below the first bin center and above the last it returns that bin's calibrated value. Between two centers it interpolates linearly. It is not a nearest-bin lookup, and it does not return the input unchanged anywhere.
The table ships inside each weightsparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. package as calibration.json, and a per-countrycountryThe top-level address component (an ISO country). Closed-vocabulary, so it is best handled by a deterministic matcher feeding a proposal rather than a retrained model head. sibling ships as
calibration-per-locale.json. The per-countrycountryThe top-level address component (an ISO country). Closed-vocabulary, so it is best handled by a deterministic matcher feeding a proposal rather than a retrained model head. file has no top-level table key, so pass one of its
tables entries to the bare-array form.
import { readFileSync } from "node:fs"
import { createCalibrator } from "@mailwoman/core/decoder"
// Replace <WEIGHTS_DIR> with your node_modules/@mailwoman/neural-weights-en-us directory.
const table = JSON.parse(readFileSync("<WEIGHTS_DIR>/calibration.json", "utf8"))
const calibrate = createCalibrator(table)
for (const raw of [0, 0.25, 0.5, 0.75, 0.9, 0.99, 1]) {
console.log(raw.toFixed(2), "->", calibrate(raw).toFixed(4))
}
Output:
0.00 -> 0.0444
0.25 -> 0.6341
0.50 -> 0.8258
0.75 -> 0.9354
0.90 -> 0.9631
0.99 -> 0.9908
1.00 -> 0.9908
A raw 0.50 is worth 0.83 after calibration, which is the under-confidence this table corrects. The curve saturates at 0.9908, the top bin's calibrated value, so a calibrated score never reaches 1.
Pass the calibrator per parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. as ParseOpts.calibrate. It is opt-in: omitting it leaves the raw
softmaxsoftmaxThe function that converts a vector of logits into a probability distribution summing to 1, applied after priors and biases are added to the emission logits. confidences, which is the byte-stable default.
Errors
| Condition | What happens | Next step |
|---|---|---|
| The weightsparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. package for a localelocaleThe combination of language and country an address comes from. en-US and fr-FR are the locales Mailwoman ships weights for. is absent | loadFromWeights throws one actionable error naming the package | npm install @mailwoman/neural-weights-<locale> |
| The weightsparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. package resolves but the modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' fails to load | loadFromWeights throws with the underlying error preserved | Reinstall the package. A partial or truncated bundleevidence bundleThe pair of retrieval-augmented input channels (street-type + locality-surface) that feed lexicon membership as soft per-token evidence alongside the text. Shipped in 6.7.0; trained natively from step 0 in the from-scratch base line. is the usual cause |
| A model cardmodel cardA JSON metadata file (model-card.json) shipped with each weights bundle. It declares the model version, lineage, label set, required inference channels (anchor, gazetteer), calibration data, and training provenance. declares a channel the package does not ship | The classifier warns once and runs that channel off | Reinstall. A soft-fed channel changes accuracy, not correctness |
createRuntimePipeline has no classifier | The pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package. runs the structural stagesstageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). only; tree may be empty | Pass a classifier |
createRuntimePipeline has no resolver | The tree carries no coordinates | Pass a createWOFResolver instance |
| The gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. index fails to deserialize | A warning on stderr, and the parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. runs without gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. priors | Reinstall the weightsparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. package |
createCalibrator gets an empty table | Throws createCalibrator: empty calibration table | Pass the parsed calibration.json, not an empty array |
geocodeAddress hits a fatal parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. or resolve error | Throws | Catch per row. Every non-fatal case returns a result on the admin tier |
Rationale
The classifier and the resolverresolverThe component that converts parsed address components (locality, region, postcode) into coordinates by looking them up in the gazetteer. The resolver ranks candidates by name match, population, and proximity, and returns the best-matching place with its centroid or polygon. are the two stagesstageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). with no default, and that is deliberate rather
than unfinished. The modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' is a 39 MB download and the gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. is 1.65 GB. A factory that acquired
either implicitly would make import "mailwoman" an unpredictable amount of disk and network for a
caller who wanted to parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. one string.
Everything else defaults on. Each mechanism that reached default-on cleared a gate against the shipped modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' on the first-class localeslocaleThe combination of language and country an address comes from. en-US and fr-FR are the locales Mailwoman ships weights for., so the shipped defaults are the measured configuration and a deviation from them is the thing that needs justifying.
false rather than omission is the disable signal for the auto-loading options because omission has
to keep meaning "give me the default". A caller who needs byte-identical output against a build
without a mechanism has one unambiguous way to say so.
See also
- CLI — the same pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package. behind a command.
- Component tags — what each tag in a parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. means.
- Runtime flags — the environment variables these functions read.
- Understand a parse — the same calls, walked through.
- Tune confidence thresholds — using the calibrator against your own data.