@mailwoman/neural-weights-de-de 8.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/model-card.json +43 -0
- package/package.json +30 -0
- package/pair-index-de.bin +0 -0
- package/scripts/link-dev-weights.ts +117 -0
package/README.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# @mailwoman/neural-weights-de-de
|
|
2
|
+
|
|
3
|
+
German (`de-DE`) weights overlay for [mailwoman](https://mailwoman.sister.software).
|
|
4
|
+
|
|
5
|
+
**Data-only.** This package ships no model of its own — it declares
|
|
6
|
+
`mailwoman.baseWeights: "@mailwoman/neural-weights-en-us"` and shares that package's `model.onnx`
|
|
7
|
+
and `tokenizer.model`. What it adds is the German **placetype-pair index**: 85,605
|
|
8
|
+
`(Ortsteil, Gemeinde)` pairs from Who's On First, applied as a soft decode-time prior so
|
|
9
|
+
`Neusser Str. 12, Nippes, 50733 Köln` resolves `Nippes` as a dependent locality instead of losing it.
|
|
10
|
+
|
|
11
|
+
## Why an overlay exists at all
|
|
12
|
+
|
|
13
|
+
The pair index is hard-gated on the resolved locale's country, so a German artifact shipped inside
|
|
14
|
+
another locale's package could never fire. A carrier package is the only way the prior reaches
|
|
15
|
+
German input — which is what blocked this locale until now, rather than any shortage of data.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
npm install @mailwoman/neural-weights-de-de
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Then resolve weights for `de-DE`; `@mailwoman/neural` picks up the sibling index automatically.
|
package/model-card.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mailwoman/neural-weights-de-de",
|
|
3
|
+
"version": "7.0.0",
|
|
4
|
+
"locale": "de-DE",
|
|
5
|
+
"license": "AGPL-3.0",
|
|
6
|
+
"$comment": "Data-only overlay. Shares model.onnx + tokenizer.model with @mailwoman/neural-weights-en-us via mailwoman.baseWeights \u2014 this package ships ONLY the DE placetype-pair index.",
|
|
7
|
+
"de_artifacts": {
|
|
8
|
+
"$comment": "Hierarchy campaign R9 (2026-08-02). The German instance of the dependent-locality prior.",
|
|
9
|
+
"pair_index_de_bin": {
|
|
10
|
+
"format": "PIX1 placetype-pair index \u2014 a retrieval-augmented (Ortsteil, Gemeinde) dependent_locality prior, e.g. (\"Nippes\", \"K\u00f6ln\")",
|
|
11
|
+
"source": "Who's On First admin hierarchy ($MAILWOMAN_DATA_ROOT/wof/admin-global-priority.db). Children: borough + neighbourhood (WOF's typing for Ortsteile/Stadtteile). Parents: locality, localadmin, borough. No postal register: Deutsche Post routes 'PLZ Stadt' and carries no Ortsteil field, so WOF is the source \u2014 the same call the US instance made and the opposite of France's, where BAN's lieu-dit field IS the postal line.",
|
|
12
|
+
"pairs": 85605,
|
|
13
|
+
"bytes": 2160665,
|
|
14
|
+
"delta_calibration": "\u03b4=10, transitionBeta=5 \u2014 inherited from the GB calibration per R5's finding that the classifier's dependent_locality deficit is uniform across locales.",
|
|
15
|
+
"country_gate": "hard-gated on the header's country (de).",
|
|
16
|
+
"requires_leading_postcode_strip": "This artifact is INERT without the `de` entries in SEGMENT_PARENT_POSTCODE_SHAPES and LEADING_POSTCODE_COUNTRIES (neural/placetype-pair-prior.ts). German addresses write '50733 K\u00f6ln', so a parent segment folds to a key no bare-Gemeinde entry matches \u2014 measured: the index alone changed nothing until both entries landed. Same dependency FR's artifact carries.",
|
|
17
|
+
"not_committed": "derived artifact, .gitignore'd \u2014 rebuilt via `mailwoman gazetteer pair-index --out neural-weights-de-de --country de --delta 10 --transition-beta 5 --pairs-jsonl <de-pairs.jsonl>`"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"eval": {
|
|
21
|
+
"r9_de_ortsteil": {
|
|
22
|
+
"venue_confound_board": {
|
|
23
|
+
"n": 70,
|
|
24
|
+
"false_positives": 0,
|
|
25
|
+
"fp_pct": 0.0,
|
|
26
|
+
"bar_pct": 2.0,
|
|
27
|
+
"note": "45 directional-class surfaces (Nord/S\u00fcd/Ober/Neu\u2026, from 9,406 available) + 25 others, each opening a German venue name"
|
|
28
|
+
},
|
|
29
|
+
"positive_board": {
|
|
30
|
+
"n": 60,
|
|
31
|
+
"emit": 60,
|
|
32
|
+
"tag_correct": 60,
|
|
33
|
+
"tag_correct_pct": 100.0,
|
|
34
|
+
"bar_pct": 70.0
|
|
35
|
+
},
|
|
36
|
+
"fusion_fix": "Without the index 'Venloer Str. 300, Ehrenfeld, 50823 K\u00f6ln' fused the locality as 'Ehrenfeld K\u00f6ln'; with it the two split correctly."
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": {
|
|
40
|
+
"model_card": "model-card.json",
|
|
41
|
+
"pair_index": "pair-index-de.bin"
|
|
42
|
+
}
|
|
43
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mailwoman/neural-weights-de-de",
|
|
3
|
+
"version": "8.5.0",
|
|
4
|
+
"description": "German (de-DE) weights overlay for mailwoman — data-only; shares the base model with @mailwoman/neural-weights-en-us and ships the DE placetype-pair index.",
|
|
5
|
+
"license": "AGPL-3.0-only OR LicenseRef-Commercial",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/sister-software/mailwoman.git",
|
|
9
|
+
"directory": "neural-weights-en-nz"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"model-card.json",
|
|
13
|
+
"pair-index-de.bin",
|
|
14
|
+
"README.md",
|
|
15
|
+
"*.ts",
|
|
16
|
+
"**/*.ts",
|
|
17
|
+
"!*.test.ts",
|
|
18
|
+
"!**/*.test.ts"
|
|
19
|
+
],
|
|
20
|
+
"type": "module",
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@mailwoman/neural-weights-en-us": "8.5.0"
|
|
26
|
+
},
|
|
27
|
+
"mailwoman": {
|
|
28
|
+
"baseWeights": "@mailwoman/neural-weights-en-us"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Dev-weights linker for `@mailwoman/neural-weights-de-de` (hierarchy campaign R9).
|
|
7
|
+
*
|
|
8
|
+
* This overlay ships exactly ONE artifact — `pair-index-de.bin` — because it declares
|
|
9
|
+
* `mailwoman.baseWeights` and shares the base model/tokenizer with the en-us package. There is
|
|
10
|
+
* nothing to symlink; the only job is building the index so `resolveWeights({locale: "de-de"})`
|
|
11
|
+
* surfaces `pairIndexPath` in local dev.
|
|
12
|
+
*
|
|
13
|
+
* The index is INERT without the `de` entries in `SEGMENT_PARENT_POSTCODE_SHAPES` and
|
|
14
|
+
* `LEADING_POSTCODE_COUNTRIES` (`neural/placetype-pair-prior.ts`): German addresses write
|
|
15
|
+
* "50733 Köln", so a parent segment folds to a key no bare-Gemeinde entry matches. Measured
|
|
16
|
+
* during R9 — the artifact alone changed nothing until both landed.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { spawnSync } from "node:child_process"
|
|
20
|
+
import { existsSync, readFileSync } from "node:fs"
|
|
21
|
+
import { resolve } from "node:path"
|
|
22
|
+
|
|
23
|
+
import { repoRootPath } from "@mailwoman/core/utils"
|
|
24
|
+
|
|
25
|
+
const PKG_DIR = String(repoRootPath("neural-weights-de-de"))
|
|
26
|
+
const CLI = String(repoRootPath("mailwoman", "out", "cli.js"))
|
|
27
|
+
const PAIR_INDEX_BIN_DEST = resolve(PKG_DIR, "pair-index-de.bin")
|
|
28
|
+
/**
|
|
29
|
+
* Checked-in WOF-derived (Ortsteil, Gemeinde) pairs — the same posture as the GB secondary sources.
|
|
30
|
+
*/
|
|
31
|
+
const DE_PAIRS_JSONL = String(repoRootPath("data", "gazetteer", "de-pairs-v1.jsonl"))
|
|
32
|
+
/**
|
|
33
|
+
* Calibrated magnitudes — the pair the R9 bars were measured at (0/70 confound FPs, 60/60 tag-correct).
|
|
34
|
+
*/
|
|
35
|
+
const PAIR_INDEX_DELTA = 10
|
|
36
|
+
const PAIR_INDEX_TRANSITION_BETA = 5
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Minimal PIX1 header reader — magic + header only, reimplemented so this data-only package gains no dependency on
|
|
40
|
+
* `@mailwoman/neural` (which pulls onnxruntime-node) to read two fields.
|
|
41
|
+
*/
|
|
42
|
+
function peekPairIndexHeaderFields(path: string): { delta: number; transitionBeta: number | undefined } {
|
|
43
|
+
const bytes = readFileSync(path)
|
|
44
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)
|
|
45
|
+
// "PIX1" little-endian.
|
|
46
|
+
const MAGIC = 0x31_58_49_50
|
|
47
|
+
|
|
48
|
+
if (view.getUint32(0, true) !== MAGIC) {
|
|
49
|
+
throw new Error(`pair index: bad magic reading ${path}`)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const headerLen = view.getUint32(4, true)
|
|
53
|
+
const header = JSON.parse(Buffer.from(bytes.subarray(8, 8 + headerLen)).toString("utf8")) as {
|
|
54
|
+
delta: number
|
|
55
|
+
transitionBeta?: number
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return { delta: header.delta, transitionBeta: header.transitionBeta }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!existsSync(CLI)) {
|
|
62
|
+
console.error(`WARNING: ${CLI} not built — run \`yarn compile\` first, then re-run for pair-index-de.bin.`)
|
|
63
|
+
} else if (!existsSync(DE_PAIRS_JSONL)) {
|
|
64
|
+
console.error(`WARNING: missing ${DE_PAIRS_JSONL} — pair-index-de.bin not built.`)
|
|
65
|
+
} else {
|
|
66
|
+
let needsRebuild = true
|
|
67
|
+
|
|
68
|
+
if (existsSync(PAIR_INDEX_BIN_DEST)) {
|
|
69
|
+
try {
|
|
70
|
+
const header = peekPairIndexHeaderFields(PAIR_INDEX_BIN_DEST)
|
|
71
|
+
|
|
72
|
+
if (header.delta !== PAIR_INDEX_DELTA) {
|
|
73
|
+
console.log(`rebuilding pair-index-de.bin — delta ${header.delta} → ${PAIR_INDEX_DELTA}`)
|
|
74
|
+
} else if (header.transitionBeta !== PAIR_INDEX_TRANSITION_BETA) {
|
|
75
|
+
console.log(
|
|
76
|
+
`rebuilding pair-index-de.bin — transitionBeta ${header.transitionBeta} → ${PAIR_INDEX_TRANSITION_BETA}`
|
|
77
|
+
)
|
|
78
|
+
} else {
|
|
79
|
+
needsRebuild = false
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.log(`rebuilding pair-index-de.bin — header unreadable (${(error as Error).message})`)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!needsRebuild) {
|
|
87
|
+
console.log(`skipped pair-index-de.bin build — ${PAIR_INDEX_BIN_DEST} is current`)
|
|
88
|
+
} else {
|
|
89
|
+
const result = spawnSync(
|
|
90
|
+
process.execPath,
|
|
91
|
+
[
|
|
92
|
+
CLI,
|
|
93
|
+
"gazetteer",
|
|
94
|
+
"pair-index",
|
|
95
|
+
"--out",
|
|
96
|
+
PKG_DIR,
|
|
97
|
+
"--country",
|
|
98
|
+
"de",
|
|
99
|
+
"--delta",
|
|
100
|
+
String(PAIR_INDEX_DELTA),
|
|
101
|
+
"--transition-beta",
|
|
102
|
+
String(PAIR_INDEX_TRANSITION_BETA),
|
|
103
|
+
"--pairs-jsonl",
|
|
104
|
+
DE_PAIRS_JSONL,
|
|
105
|
+
],
|
|
106
|
+
{ stdio: "inherit" }
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
if (result.status !== 0 || !existsSync(PAIR_INDEX_BIN_DEST)) {
|
|
110
|
+
console.error(`FAILED: gazetteer pair-index --country de (exit ${result.status})`)
|
|
111
|
+
|
|
112
|
+
process.exit(1)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.log(`built pair-index-de.bin ← ${DE_PAIRS_JSONL}`)
|
|
116
|
+
}
|
|
117
|
+
}
|