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 and 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, and
createCalibrator. Each section contains 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 check, 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-hint, @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-hint → 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 decision-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 states that and lists what it left out.
Import map
These are the import paths a consumer reaches for. One of them 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 exports |
|---|---|
mailwoman | createRuntimePipeline, plus everything @mailwoman/core re-exports |
mailwoman/geocode | geocodeAddress, parseForGeocode, USStateDatabaseProvider, geocodeStream, the result types |
mailwoman/resolver-backend | createResolverBackend and resolveCandidateDBPath |
@mailwoman/core/data-root | dataRootPath and the other data-root path helpers |
mailwoman/cli/kit | Ink helpers for building an interactive command |
mailwoman/test-kit | The parser test helpers |
mailwoman/poi | The POI intent stagestageOne of the dataflow stages in the runtime pipeline (normalize, locale check, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). and executor |
@mailwoman/poi-taxonomy/overpass | emitOverpassQL, the 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. 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 rather than in the factory.
Options
Two stagesstageOne of the dataflow stages in the runtime pipeline (normalize, locale check, 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 have production defaults.
| Option | Type | Default | Effect |
|---|---|---|---|
classifier | AddressClassifier | none | StagestageOne of the dataflow stages in the runtime pipeline (normalize, locale check, 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-hint → 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 check, 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 check, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). 6. Without it the tree has no coordinates |
fst | "auto", "none" or a matcher | "auto" | 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 | "auto", "none" or a matcher | "auto" | Signal source for 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.-context decision |
streetEvidence | "auto", "none" or a StreetLocalityEvidence | "auto" | 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 | "auto", "none" or a function | "auto": 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 | "extract", "none" or { poiDatabasePath } | "extract" | 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 extractsextractA partial output file of the corpus build, written in Parquet format. The training pipeline streams extracts row by row. its intent. The object form also executes it |
caseNormalization | "title-case" or "preserve" | "title-case" | 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.' |
placerCountryUse | "filter" | "prior" | "filter" | "filter" 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-hint | StagestageOne of the dataflow stages in the runtime pipeline (normalize, locale check, 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 check, 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 check, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). 2.7 |
A "none" value for fst, streetMorphology, streetEvidence, placeCountry or poiQueryKind
suppresses the auto-load. That is how a caller gets output identical to a build without that mechanism.
machinePreferences takes the same "auto", "none" or explicit value.
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 locale-hint | BCP-47 tag, for example en-US |
inputMode | "fragmented", "formatted" or "auto" | "auto": 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. |
caseNormalization | "title-case" or "preserve" | the factory value | Overrides the factory default for this call |
placetypePair | "inherit", "off" or a prior | "inherit" | The classifier's placetypeplacetypeThe Who's On First hierarchical classification of places: planet → continent → country → region → county → locality → neighborhood. The resolver uses placetype to rank candidates — an exact locality match outranks a county-level match.-pair prior for this call |
placerCountryUse | "filter" | "prior" | the factory value | Overrides the factory default for this call |
forceFullPipeline | boolean | off | Disables the structural fast paths. A debugging aid |
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 check, 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 check, 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 check, 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 | POIQueryResult 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 check, 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-hint → 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 check, 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. That value 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 check, 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 check, 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 contains 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 decision thresholds (fstBiasScale, fstStreetContextRequirement 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. |
caseNormalization | "title-case" or "preserve" | "title-case" | 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 | "off" or WordConsistencyOpts | the shipped settings | Per-word BIO vote, skipping byte-fallback words. {} runs the unthresholded vote |
postcodeRepair | boolean | off | Deterministic postcodepostcodeThe country-specific postcode (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 | "inherit", "auto", "none" or a config | "inherit": the classifier's proposer | 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. "auto" uses the default proposer even if the config disabled it |
addressSystemConventions | "inherit", "off", "auto" or a system code | "inherit": 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 | "inherit", "off" or PlacetypePairPriorOpts | "inherit": 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 | "off" disables the prior for the call. A prior object replaces the package value |
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-hint → 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-hint → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package..
Pass the gazetteer index to 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.. No other code
loads it for you outside createRuntimePipeline: the pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-hint → 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 rather than a stagestageOne of the dataflow stages in the runtime pipeline (normalize, locale check, 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 readFile.
import { readFile } from "node:fs/promises"
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(await readFile(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-hint → 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 matcher 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.
Resolve coordinates
mailwoman/geocode 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 databases 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; osmExtracts 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" or "auto" | "auto": 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 |
extracts | RegionDatabaseResolver | 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 |
nationalExtracts | (country) => RegionDatabases | 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. lookups, 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. database |
defaultCountry | { country, source: "caller" | "inferred" } | 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.. "inferred" marks a localelocaleThe combination of language and country an address comes from. en-US and fr-FR are the locales Mailwoman ships weights for. guess that stronger evidence can override |
bias | proximity points | none | Soft re-rank toward a viewport or a user location |
interpCalibration | number or a table | the database's 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 |
caseNormalization | "title-case" or "preserve" | "title-case" | 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 check, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). |
placeCountry | "auto", "none" or a function | "auto": 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 |
placerCountryUse | "filter" | "prior" | "filter" | "filter" hard-filters 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 postcode (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. |
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. Condition any house-grade rendering on
the tier rather than 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 postcode (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 contains 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.
USStateDatabaseProvider
USStateDatabaseProvider 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. It implements the RegionDatabaseProvider interface from
@mailwoman/core/resolver, which 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. providers in @mailwoman/ban and @mailwoman/osm share.
class USStateDatabaseProvider implements RegionDatabaseProvider {
static create(factory: RegionDatabaseFactory, dataRoot: PathBuilderLike): Promise<USStateDatabaseProvider>
readonly for: RegionDatabaseResolver
versions(): DataReleaseManifest | null
reload(): Promise<DataReleaseManifest | null>
close(): void
[Symbol.dispose](): void
}
The constructor is private; create reads the release manifest and warms the path map before it
returns. Its arguments are 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.extracts.
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. databases 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"
const classifier = await NeuralAddressClassifier.loadFromWeights({ locale: "en-US" })
// Replace <CANDIDATE_DB> with the path `mailwoman data pull candidate` printed.
using 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))
Output:
{
"input": "350 5th Ave, New York, NY 10118",
"lat": 40.694457,
"lon": -73.93045,
"resolution_tier": "admin",
"epistemic_status": "observed",
"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 rather than 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 { readFile } from "node:fs/promises"
import { createCalibrator } from "@mailwoman/core/decoder"
// Replace <WEIGHTS_DIR> with your node_modules/@mailwoman/neural-weights-en-us directory.
const table = JSON.parse(await readFile("<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 becomes 0.83 after calibration. That gap 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. Those are 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 rather than correctness |
createRuntimePipeline has no classifier | The pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-hint → 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 check, 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 has 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 rather than 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 check, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). with no default. The omission 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 2.88 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 an evaluationevalRunning the model against a held-out golden dataset and computing per-component F1, exact-match, calibration, and resolved-coordinate error. 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-hint → 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.