@mailwoman/neural 2.1.0 → 4.0.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.
Files changed (72) hide show
  1. package/out/anchor-inference.d.ts +57 -0
  2. package/out/anchor-inference.d.ts.map +1 -0
  3. package/out/anchor-inference.js +94 -0
  4. package/out/anchor-inference.js.map +1 -0
  5. package/out/browser.d.ts +18 -0
  6. package/out/browser.d.ts.map +1 -0
  7. package/out/browser.js +19 -0
  8. package/out/browser.js.map +1 -0
  9. package/out/classifier.d.ts +145 -11
  10. package/out/classifier.d.ts.map +1 -1
  11. package/out/classifier.js +185 -20
  12. package/out/classifier.js.map +1 -1
  13. package/out/fst-prior.d.ts +71 -0
  14. package/out/fst-prior.d.ts.map +1 -0
  15. package/out/fst-prior.js +173 -0
  16. package/out/fst-prior.js.map +1 -0
  17. package/out/index.d.ts +7 -0
  18. package/out/index.d.ts.map +1 -1
  19. package/out/index.js +5 -0
  20. package/out/index.js.map +1 -1
  21. package/out/labels.d.ts +30 -6
  22. package/out/labels.d.ts.map +1 -1
  23. package/out/labels.js +43 -6
  24. package/out/labels.js.map +1 -1
  25. package/out/onnx-runner.d.ts +8 -1
  26. package/out/onnx-runner.d.ts.map +1 -1
  27. package/out/onnx-runner.js +31 -1
  28. package/out/onnx-runner.js.map +1 -1
  29. package/out/postcode-anchor.d.ts +117 -0
  30. package/out/postcode-anchor.d.ts.map +1 -0
  31. package/out/postcode-anchor.js +269 -0
  32. package/out/postcode-anchor.js.map +1 -0
  33. package/out/postcode-binary-resolver.d.ts +60 -0
  34. package/out/postcode-binary-resolver.d.ts.map +1 -0
  35. package/out/postcode-binary-resolver.js +208 -0
  36. package/out/postcode-binary-resolver.js.map +1 -0
  37. package/out/postcode-repair.d.ts +65 -0
  38. package/out/postcode-repair.d.ts.map +1 -0
  39. package/out/postcode-repair.js +171 -0
  40. package/out/postcode-repair.js.map +1 -0
  41. package/out/proposal-classifier.d.ts +5 -1
  42. package/out/proposal-classifier.d.ts.map +1 -1
  43. package/out/proposal-classifier.js +5 -3
  44. package/out/proposal-classifier.js.map +1 -1
  45. package/out/query-shape-prior.d.ts +74 -0
  46. package/out/query-shape-prior.d.ts.map +1 -0
  47. package/out/query-shape-prior.js +223 -0
  48. package/out/query-shape-prior.js.map +1 -0
  49. package/out/street-morphology-prior.d.ts +56 -0
  50. package/out/street-morphology-prior.d.ts.map +1 -0
  51. package/out/street-morphology-prior.js +159 -0
  52. package/out/street-morphology-prior.js.map +1 -0
  53. package/out/tokenizer.d.ts +6 -1
  54. package/out/tokenizer.d.ts.map +1 -1
  55. package/out/tokenizer.js +8 -3
  56. package/out/tokenizer.js.map +1 -1
  57. package/out/unit-repair.d.ts +46 -0
  58. package/out/unit-repair.d.ts.map +1 -0
  59. package/out/unit-repair.js +147 -0
  60. package/out/unit-repair.js.map +1 -0
  61. package/out/viterbi.d.ts +76 -0
  62. package/out/viterbi.d.ts.map +1 -0
  63. package/out/viterbi.js +163 -0
  64. package/out/viterbi.js.map +1 -0
  65. package/out/vitest.config.d.ts.map +1 -1
  66. package/out/vitest.config.js +3 -0
  67. package/out/vitest.config.js.map +1 -1
  68. package/out/weights.d.ts +42 -0
  69. package/out/weights.d.ts.map +1 -1
  70. package/out/weights.js +92 -4
  71. package/out/weights.js.map +1 -1
  72. package/package.json +10 -3
@@ -0,0 +1,147 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Secondary-unit regex repair pass — parser-improvement backlog (2026-05-30).
7
+ *
8
+ * The three-arena capability eval surfaced a persistent neural weakness: the
9
+ * model DROPS secondary units. "123 Main St Apt 456" → no unit label; the
10
+ * postal-standards secondary-unit edge class scored 0% neural. Units have a
11
+ * rigid surface shape (a designator keyword + an identifier), so — exactly
12
+ * like the postcode-repair pass (#35) — we can detect them deterministically
13
+ * and repair the BIO labels AFTER decode but BEFORE `buildAddressTree`. The
14
+ * model is untouched; this is a decoder-side correction, the same "lowest
15
+ * risk" lever family as postcode-repair.
16
+ *
17
+ * PRECISION GUARDS (mirror postcode-repair — never regress a confident parse):
18
+ * - We only fire on EXPLICIT designators (Apt, Ste, Suite, Unit, Rm, Floor,
19
+ * Bldg, Flat, … + bare "#<n>"). Ambiguous tokens are deliberately excluded:
20
+ * "Box" (that's po_box), bare "F"/"No" (too greedy), "Space"/"Stop" (common
21
+ * words).
22
+ * - ADD path (model emitted no unit over the matched run): allowed ONLY over
23
+ * `O` tokens — never over house_number / street* / postcode / po_box / a
24
+ * geographic container. So a confidently-labeled street or number is safe.
25
+ * - SNAP path: when the model already started a unit span inside the match,
26
+ * we expand/clip it to the full detected shape.
27
+ * - Local smear-clip: unit tokens immediately flanking a snapped run are
28
+ * cleared (mirrors postcode-repair) so "Apt 4 Springfield" can't leave a
29
+ * stray I-unit on "Springfield".
30
+ *
31
+ * Opt-in via `ParseOpts.unitRepair` (postcode-repair earned default-on only
32
+ * after a measured +135/0; unit-repair stays opt-in until the v0.7.2 arena
33
+ * re-run quantifies its delta).
34
+ */
35
+ /**
36
+ * Secondary-unit shape patterns, ordered most-specific → least. Case-insensitive
37
+ * (unit designators appear in every casing in real data). The identifier is a
38
+ * 1-5 digit number with an optional trailing letter ("4B"), a single letter
39
+ * ("STE D"), or a letter+digits — kept tight so we don't swallow following words.
40
+ */
41
+ const UNIT_DESIGNATORS = "APARTMENT|APT|SUITE|STE|UNIT|ROOM|RM|FLOOR|FLR|FL|BUILDING|BLDG|DEPARTMENT|DEPT|LOT|TRAILER|TRLR|SLIP|HANGAR|PIER|FLAT|PH|PENTHOUSE";
42
+ const UNIT_PATTERNS = [
43
+ // Designator + optional "#"/"No." + identifier, e.g. "Apt 4B", "Ste 12", "STE D",
44
+ // "Unit 9400", "Suite 100", "Rm 5", "Flat 2", "Apartment #3", "Bldg C".
45
+ // The `\b` after the designator is load-bearing: it stops "Unit" matching inside
46
+ // "United", "Fl" inside "Florida", etc. The trailing `\b` on the identifier stops
47
+ // "Apt Main" capturing the "M" of "Main" (single-letter ident only fires on a
48
+ // standalone token like "STE D").
49
+ {
50
+ label: "designator",
51
+ re: new RegExp(`\\b(?:${UNIT_DESIGNATORS})\\b\\.?\\s*#?\\s*(?:No\\.?\\s*)?(?:\\d{1,5}[A-Za-z]?|[A-Za-z]\\d{0,4})\\b`, "gi"),
52
+ },
53
+ // Bare hash + identifier, e.g. "#104", "# 4B". Common US secondary-unit form.
54
+ { label: "hash", re: /#\s*\d{1,5}[A-Za-z]?\b/g },
55
+ ];
56
+ const UNIT_B = "B-unit";
57
+ const UNIT_I = "I-unit";
58
+ const OUTSIDE = "O";
59
+ /**
60
+ * Tags a unit span is allowed to overwrite on the ADD path. The v0.7.2 arena
61
+ * showed the dominant failure for bare designator-led units ("Flat 2 14 Smith
62
+ * St", "APT 2 …") is the model labeling the WHOLE designator+identifier run as
63
+ * `locality` — not leaving it `O`. An explicit designator + identifier is a
64
+ * high-confidence "this is a unit" shape (a real locality/suburb name never has
65
+ * that form), so — exactly like postcode-repair's ADD_OVER_TAGS — we let it
66
+ * reclaim a `locality`/`dependent_locality` span. Structural tags
67
+ * (house_number, street*, postcode, po_box, region, country, venue) stay off the
68
+ * list so a confident parse is never clobbered. (`O` is always eligible.)
69
+ */
70
+ const ADD_OVER_TAGS = new Set(["locality", "dependent_locality"]);
71
+ function isUnitLabel(label) {
72
+ return label === "B-unit" || label === "I-unit";
73
+ }
74
+ /** Extract the bare tag from a BIO label ("B-locality" → "locality", "O" → null). */
75
+ function tagOf(label) {
76
+ return label === "O" ? null : label.slice(2);
77
+ }
78
+ /** Collect non-overlapping unit matches, preferring more-specific (earlier) patterns + longest. */
79
+ function collectMatches(text) {
80
+ const candidates = [];
81
+ UNIT_PATTERNS.forEach((pat, priority) => {
82
+ pat.re.lastIndex = 0;
83
+ for (let m = pat.re.exec(text); m; m = pat.re.exec(text)) {
84
+ candidates.push({ start: m.index, end: m.index + m[0].length, priority });
85
+ }
86
+ });
87
+ // Longest-match-wins, then most-specific; reject anything overlapping an accepted match.
88
+ candidates.sort((a, b) => b.end - b.start - (a.end - a.start) || a.priority - b.priority);
89
+ const accepted = [];
90
+ for (const c of candidates) {
91
+ if (accepted.some((a) => c.start < a.end && a.start < c.end))
92
+ continue;
93
+ accepted.push(c);
94
+ }
95
+ return accepted;
96
+ }
97
+ /**
98
+ * Repair secondary-unit label spans in a decoded token sequence using designator
99
+ * regexes. Returns a NEW token array (inputs are not mutated) plus a change count.
100
+ */
101
+ export function repairUnitLabels(text, input) {
102
+ const matches = collectMatches(text);
103
+ const tokens = input.map((t) => ({ ...t }));
104
+ if (matches.length === 0)
105
+ return { tokens, changed: 0 };
106
+ let changed = 0;
107
+ const setLabel = (i, label) => {
108
+ if (tokens[i].label !== label) {
109
+ tokens[i].label = label;
110
+ changed++;
111
+ }
112
+ };
113
+ for (const m of matches) {
114
+ // Tokens whose char span intersects the match.
115
+ const overlap = [];
116
+ for (let i = 0; i < tokens.length; i++) {
117
+ const t = tokens[i];
118
+ if (t.start < m.end && m.start < t.end)
119
+ overlap.push(i);
120
+ }
121
+ if (overlap.length === 0)
122
+ continue;
123
+ const hasUnit = overlap.some((i) => isUnitLabel(tokens[i].label));
124
+ if (!hasUnit) {
125
+ // ADD path — explicit designators are high-confidence, but only ever over O or a
126
+ // geographic-container tag (locality/dependent_locality — the tags the model
127
+ // mislabels bare units as). Never clobber a confident house_number/street/postcode/
128
+ // po_box/region/country/venue.
129
+ const safe = overlap.every((i) => {
130
+ const tag = tagOf(tokens[i].label);
131
+ return tag === null || ADD_OVER_TAGS.has(tag);
132
+ });
133
+ if (!safe)
134
+ continue;
135
+ }
136
+ // SNAP/ADD: relabel the matched run as a single unit span.
137
+ overlap.forEach((i, k) => setLabel(i, k === 0 ? UNIT_B : UNIT_I));
138
+ // Local smear clip: clear unit tokens immediately flanking the snapped run.
139
+ for (let j = overlap[0] - 1; j >= 0 && isUnitLabel(tokens[j].label); j--)
140
+ setLabel(j, OUTSIDE);
141
+ for (let j = overlap[overlap.length - 1] + 1; j < tokens.length && isUnitLabel(tokens[j].label); j++) {
142
+ setLabel(j, OUTSIDE);
143
+ }
144
+ }
145
+ return { tokens, changed };
146
+ }
147
+ //# sourceMappingURL=unit-repair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unit-repair.js","sourceRoot":"","sources":["../unit-repair.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Linear-chain CRF Viterbi decoder in TypeScript.
7
+ *
8
+ * Replaces per-token argmax in the classifier when transition scores are available. Mirrors the
9
+ * Python training-time / eval-time path so JS runtime decode agrees with the model card's
10
+ * metrics.
11
+ *
12
+ * Two transition matrix modes:
13
+ *
14
+ * 1. **Structural-only** (no weights changes required) — build from the BIO label vocabulary using
15
+ * `buildBioTransitionMask()`. Forbids `O → I-X`, `B-X → I-Y` (X ≠ Y), and sequence-start →
16
+ * `I-X`. Permits everything else. This alone prevents orphan-I decoding ("Saint Petersburg →
17
+ * Petersburg" bug) at runtime — a strict improvement over argmax.
18
+ * 2. **Learned** (requires a future weights release that ships `crf-transitions.json`) — load the
19
+ * trained transition matrix from the model card. Adds learned soft priors on top of the
20
+ * structural mask. Currently not exported from the training-side ONNX bundle.
21
+ */
22
+ /**
23
+ * Build the BIO structural transition mask given the label vocabulary in order.
24
+ *
25
+ * Rules:
26
+ *
27
+ * - `X → O` always permitted (0)
28
+ * - `X → B-Y` always permitted (0)
29
+ * - `X → I-Y` permitted only if `X` is `B-Y` or `I-Y` (0); otherwise -inf
30
+ *
31
+ * Returns a `numLabels × numLabels` matrix where `mask[from][to]` is the additive log-score (0 for
32
+ * permitted, NEG_INF for forbidden).
33
+ */
34
+ export declare function buildBioTransitionMask(labels: readonly string[]): number[][];
35
+ /** Returns the per-label vector of valid start-of-sequence transitions (0 or -inf). */
36
+ export declare function buildBioStartMask(labels: readonly string[]): number[];
37
+ /**
38
+ * End-of-sequence transitions. By default all labels are valid endings (returns zeros). Override if
39
+ * the trained model has learned end transitions.
40
+ */
41
+ export declare function buildBioEndMask(labels: readonly string[]): number[];
42
+ export interface ViterbiInput {
43
+ /** `emissions[t][k]` — log-emission for label k at timestep t. Pass raw logits or log-softmaxes. */
44
+ emissions: number[][];
45
+ /** `transitions[from][to]` — additive log-score. Use `buildBioTransitionMask` if unsure. */
46
+ transitions: number[][];
47
+ /** Per-label log-score for being the FIRST label. */
48
+ startTransitions?: number[];
49
+ /** Per-label log-score for being the LAST label. */
50
+ endTransitions?: number[];
51
+ }
52
+ export interface ViterbiResult {
53
+ /** Best label index per timestep. */
54
+ path: number[];
55
+ /** Total path score (log-prob). */
56
+ score: number;
57
+ }
58
+ /**
59
+ * Viterbi decode: find the highest-scoring label sequence under the CRF.
60
+ *
61
+ * Time: O(seq_len × num_labels²). Space: O(seq_len × num_labels) for the backpointer table.
62
+ */
63
+ export declare function viterbi(input: ViterbiInput): ViterbiResult;
64
+ /**
65
+ * Convenience: argmax over per-token softmax (existing behavior). Provided so callers can opt in to
66
+ * Viterbi only when transitions are available, falling back to this cleanly.
67
+ */
68
+ export declare function perTokenArgmax(emissions: readonly number[][]): number[];
69
+ /**
70
+ * Softmax of a logit row (returns probabilities summing to 1).
71
+ *
72
+ * Used to compute per-token confidence after Viterbi picks the label sequence — the confidence is
73
+ * the softmax probability of the Viterbi-chosen label at that timestep.
74
+ */
75
+ export declare function softmax(row: readonly number[]): number[];
76
+ //# sourceMappingURL=viterbi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viterbi.d.ts","sourceRoot":"","sources":["../viterbi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,EAAE,CAa5E;AAED,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAErE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAEnE;AAYD,MAAM,WAAW,YAAY;IAC5B,oGAAoG;IACpG,SAAS,EAAE,MAAM,EAAE,EAAE,CAAA;IACrB,4FAA4F;IAC5F,WAAW,EAAE,MAAM,EAAE,EAAE,CAAA;IACvB,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;CACzB;AAED,MAAM,WAAW,aAAa;IAC7B,qCAAqC;IACrC,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAA;CACb;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,aAAa,CA4D1D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,CAYvE;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAMxD"}
package/out/viterbi.js ADDED
@@ -0,0 +1,163 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Linear-chain CRF Viterbi decoder in TypeScript.
7
+ *
8
+ * Replaces per-token argmax in the classifier when transition scores are available. Mirrors the
9
+ * Python training-time / eval-time path so JS runtime decode agrees with the model card's
10
+ * metrics.
11
+ *
12
+ * Two transition matrix modes:
13
+ *
14
+ * 1. **Structural-only** (no weights changes required) — build from the BIO label vocabulary using
15
+ * `buildBioTransitionMask()`. Forbids `O → I-X`, `B-X → I-Y` (X ≠ Y), and sequence-start →
16
+ * `I-X`. Permits everything else. This alone prevents orphan-I decoding ("Saint Petersburg →
17
+ * Petersburg" bug) at runtime — a strict improvement over argmax.
18
+ * 2. **Learned** (requires a future weights release that ships `crf-transitions.json`) — load the
19
+ * trained transition matrix from the model card. Adds learned soft priors on top of the
20
+ * structural mask. Currently not exported from the training-side ONNX bundle.
21
+ */
22
+ const NEG_INF = -1e9;
23
+ /**
24
+ * Build the BIO structural transition mask given the label vocabulary in order.
25
+ *
26
+ * Rules:
27
+ *
28
+ * - `X → O` always permitted (0)
29
+ * - `X → B-Y` always permitted (0)
30
+ * - `X → I-Y` permitted only if `X` is `B-Y` or `I-Y` (0); otherwise -inf
31
+ *
32
+ * Returns a `numLabels × numLabels` matrix where `mask[from][to]` is the additive log-score (0 for
33
+ * permitted, NEG_INF for forbidden).
34
+ */
35
+ export function buildBioTransitionMask(labels) {
36
+ const n = labels.length;
37
+ const mask = [];
38
+ for (let from = 0; from < n; from++) {
39
+ const row = new Array(n);
40
+ const fromLabel = labels[from];
41
+ for (let to = 0; to < n; to++) {
42
+ const toLabel = labels[to];
43
+ row[to] = isValidTransition(fromLabel, toLabel) ? 0 : NEG_INF;
44
+ }
45
+ mask.push(row);
46
+ }
47
+ return mask;
48
+ }
49
+ /** Returns the per-label vector of valid start-of-sequence transitions (0 or -inf). */
50
+ export function buildBioStartMask(labels) {
51
+ return labels.map((l) => (l.startsWith("I-") ? NEG_INF : 0));
52
+ }
53
+ /**
54
+ * End-of-sequence transitions. By default all labels are valid endings (returns zeros). Override if
55
+ * the trained model has learned end transitions.
56
+ */
57
+ export function buildBioEndMask(labels) {
58
+ return labels.map(() => 0);
59
+ }
60
+ function isValidTransition(from, to) {
61
+ if (to === "O")
62
+ return true;
63
+ if (to.startsWith("B-"))
64
+ return true;
65
+ if (to.startsWith("I-")) {
66
+ const tag = to.slice(2);
67
+ return from === `B-${tag}` || from === `I-${tag}`;
68
+ }
69
+ return true;
70
+ }
71
+ /**
72
+ * Viterbi decode: find the highest-scoring label sequence under the CRF.
73
+ *
74
+ * Time: O(seq_len × num_labels²). Space: O(seq_len × num_labels) for the backpointer table.
75
+ */
76
+ export function viterbi(input) {
77
+ const { emissions, transitions } = input;
78
+ const T = emissions.length;
79
+ if (T === 0)
80
+ return { path: [], score: 0 };
81
+ const numLabels = emissions[0].length;
82
+ const startTrans = input.startTransitions ?? new Array(numLabels).fill(0);
83
+ const endTrans = input.endTransitions ?? new Array(numLabels).fill(0);
84
+ // dp[t][k] = best log-score ending at (timestep t, label k)
85
+ const dp = [];
86
+ const back = [];
87
+ // t = 0
88
+ const first = new Array(numLabels);
89
+ for (let k = 0; k < numLabels; k++) {
90
+ first[k] = startTrans[k] + emissions[0][k];
91
+ }
92
+ dp.push(first);
93
+ back.push(new Array(numLabels).fill(-1));
94
+ for (let t = 1; t < T; t++) {
95
+ const cur = new Array(numLabels);
96
+ const ptr = new Array(numLabels);
97
+ for (let k = 0; k < numLabels; k++) {
98
+ let bestScore = NEG_INF;
99
+ let bestPrev = 0;
100
+ for (let j = 0; j < numLabels; j++) {
101
+ const s = dp[t - 1][j] + transitions[j][k];
102
+ if (s > bestScore) {
103
+ bestScore = s;
104
+ bestPrev = j;
105
+ }
106
+ }
107
+ cur[k] = bestScore + emissions[t][k];
108
+ ptr[k] = bestPrev;
109
+ }
110
+ dp.push(cur);
111
+ back.push(ptr);
112
+ }
113
+ // Pick the best ending state.
114
+ let bestEndScore = NEG_INF;
115
+ let bestEnd = 0;
116
+ for (let k = 0; k < numLabels; k++) {
117
+ const s = dp[T - 1][k] + endTrans[k];
118
+ if (s > bestEndScore) {
119
+ bestEndScore = s;
120
+ bestEnd = k;
121
+ }
122
+ }
123
+ // Trace back.
124
+ const path = new Array(T);
125
+ path[T - 1] = bestEnd;
126
+ for (let t = T - 1; t > 0; t--) {
127
+ path[t - 1] = back[t][path[t]];
128
+ }
129
+ return { path, score: bestEndScore };
130
+ }
131
+ /**
132
+ * Convenience: argmax over per-token softmax (existing behavior). Provided so callers can opt in to
133
+ * Viterbi only when transitions are available, falling back to this cleanly.
134
+ */
135
+ export function perTokenArgmax(emissions) {
136
+ return emissions.map((row) => {
137
+ let bestIdx = 0;
138
+ let bestVal = row[0];
139
+ for (let k = 1; k < row.length; k++) {
140
+ if (row[k] > bestVal) {
141
+ bestVal = row[k];
142
+ bestIdx = k;
143
+ }
144
+ }
145
+ return bestIdx;
146
+ });
147
+ }
148
+ /**
149
+ * Softmax of a logit row (returns probabilities summing to 1).
150
+ *
151
+ * Used to compute per-token confidence after Viterbi picks the label sequence — the confidence is
152
+ * the softmax probability of the Viterbi-chosen label at that timestep.
153
+ */
154
+ export function softmax(row) {
155
+ let max = row[0];
156
+ for (let i = 1; i < row.length; i++)
157
+ if (row[i] > max)
158
+ max = row[i];
159
+ const exps = row.map((v) => Math.exp(v - max));
160
+ const sum = exps.reduce((a, b) => a + b, 0);
161
+ return exps.map((e) => e / sum);
162
+ }
163
+ //# sourceMappingURL=viterbi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viterbi.js","sourceRoot":"","sources":["../viterbi.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"vitest.config.d.ts","sourceRoot":"","sources":["../vitest.config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;;AAUH,wBAiBE"}
1
+ {"version":3,"file":"vitest.config.d.ts","sourceRoot":"","sources":["../vitest.config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;;AAUH,wBAoBE"}
@@ -22,6 +22,9 @@ export default defineConfig({
22
22
  },
23
23
  { find: /^@mailwoman\/core\/(.+)$/, replacement: resolve(here, "../core/$1/index.ts") },
24
24
  { find: /^@mailwoman\/core$/, replacement: resolve(here, "../core/index.ts") },
25
+ // @mailwoman/codex resolves to source too (per-address-system postal reference data).
26
+ { find: /^@mailwoman\/codex\/(.+)$/, replacement: resolve(here, "../codex/$1/index.ts") },
27
+ { find: /^@mailwoman\/codex$/, replacement: resolve(here, "../codex/index.ts") },
25
28
  ],
26
29
  },
27
30
  test: {
@@ -1 +1 @@
1
- {"version":3,"file":"vitest.config.js","sourceRoot":"","sources":["../vitest.config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,uCAAuC;AAEvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AAEnC,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAEzD,eAAe,YAAY,CAAC;IAC3B,OAAO,EAAE;QACR,KAAK,EAAE;YACN,+EAA+E;YAC/E,0EAA0E;YAC1E;gBACC,IAAI,EAAE,0CAA0C;gBAChD,WAAW,EAAE,OAAO,CAAC,IAAI,EAAE,qCAAqC,CAAC;aACjE;YACD,EAAE,IAAI,EAAE,0BAA0B,EAAE,WAAW,EAAE,OAAO,CAAC,IAAI,EAAE,qBAAqB,CAAC,EAAE;YACvF,EAAE,IAAI,EAAE,oBAAoB,EAAE,WAAW,EAAE,OAAO,CAAC,IAAI,EAAE,kBAAkB,CAAC,EAAE;SAC9E;KACD;IACD,IAAI,EAAE;QACL,OAAO,EAAE,KAAK;QACd,OAAO,EAAE,CAAC,oBAAoB,EAAE,WAAW,EAAE,YAAY,CAAC;KAC1D;CACD,CAAC,CAAA"}
1
+ {"version":3,"file":"vitest.config.js","sourceRoot":"","sources":["../vitest.config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,uCAAuC;AAEvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AAEnC,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAEzD,eAAe,YAAY,CAAC;IAC3B,OAAO,EAAE;QACR,KAAK,EAAE;YACN,+EAA+E;YAC/E,0EAA0E;YAC1E;gBACC,IAAI,EAAE,0CAA0C;gBAChD,WAAW,EAAE,OAAO,CAAC,IAAI,EAAE,qCAAqC,CAAC;aACjE;YACD,EAAE,IAAI,EAAE,0BAA0B,EAAE,WAAW,EAAE,OAAO,CAAC,IAAI,EAAE,qBAAqB,CAAC,EAAE;YACvF,EAAE,IAAI,EAAE,oBAAoB,EAAE,WAAW,EAAE,OAAO,CAAC,IAAI,EAAE,kBAAkB,CAAC,EAAE;YAC9E,sFAAsF;YACtF,EAAE,IAAI,EAAE,2BAA2B,EAAE,WAAW,EAAE,OAAO,CAAC,IAAI,EAAE,sBAAsB,CAAC,EAAE;YACzF,EAAE,IAAI,EAAE,qBAAqB,EAAE,WAAW,EAAE,OAAO,CAAC,IAAI,EAAE,mBAAmB,CAAC,EAAE;SAChF;KACD;IACD,IAAI,EAAE;QACL,OAAO,EAAE,KAAK;QACd,OAAO,EAAE,CAAC,oBAAoB,EAAE,WAAW,EAAE,YAAY,CAAC;KAC1D;CACD,CAAC,CAAA"}
package/out/weights.d.ts CHANGED
@@ -28,12 +28,54 @@ export interface ResolveWeightsOpts {
28
28
  modelPath?: string;
29
29
  /** Explicit tokenizer.model path; takes precedence over package auto-resolve. */
30
30
  tokenizerPath?: string;
31
+ /**
32
+ * Explicit `model-card.json` path (for the label vocab) on the explicit model+tokenizer path.
33
+ * When omitted, falls back to a `model-card.json` co-located with `modelPath`. Without a card,
34
+ * labels default to `STAGE2_BIO_LABELS` — which silently mis-decodes a STAGE3 (33-label) model
35
+ * into empty/garbage parses. Pass this (or co-locate the card) when evaluating a custom STAGE3
36
+ * checkpoint via explicit paths.
37
+ */
38
+ modelCardPath?: string;
31
39
  }
32
40
  export interface ResolvedWeights {
33
41
  modelPath: string;
34
42
  tokenizerPath: string;
43
+ /**
44
+ * Path to `model-card.json` for the resolved model. On the package path, the card co-located in
45
+ * the package dir. On the explicit path, `opts.modelCardPath` or a card co-located with
46
+ * `modelPath`. `undefined` only when no card is found. Read by `loadFromWeights` to thread the
47
+ * trained label vocabulary into the classifier — see {@link readLabelsFromModelCard}.
48
+ */
49
+ modelCardPath?: string;
50
+ /**
51
+ * Path to `crf-transitions.json` alongside the resolved model. `undefined` when the file doesn't
52
+ * exist (pre-v0.6.0 bundles or CE-only training).
53
+ */
54
+ crfTransitionsPath?: string;
35
55
  /** "explicit" if both paths came from opts; "package:<name>" if resolved via require.resolve. */
36
56
  source: string;
37
57
  }
38
58
  export declare function resolveWeights(opts: ResolveWeightsOpts): ResolvedWeights;
59
+ /**
60
+ * Read the `labels` array from a `model-card.json` file. Returns `undefined` when the file is
61
+ * missing, unreadable, malformed, or has no `labels` field — callers should fall back to their
62
+ * compile-time default in that case (the loader contract: the JS-side default tracks the most
63
+ * recent shipped stage, so a card without `labels` is always a pre-v0.4.0 card whose label vocab
64
+ * matches that default by construction).
65
+ *
66
+ * Validates shape: must be a non-empty array of strings. Throws on a present-but-malformed `labels`
67
+ * field — a card that emits e.g. `labels: 21` rather than `labels: [...]` is a corrupted artifact
68
+ * and should be loud, not silently re-defaulted.
69
+ */
70
+ export declare function readLabelsFromModelCard(modelCardPath: string | undefined): readonly string[] | undefined;
71
+ export interface CrfTransitions {
72
+ transitions: number[][];
73
+ startTransitions: number[];
74
+ endTransitions: number[];
75
+ }
76
+ /**
77
+ * Read learned CRF transition parameters from `crf-transitions.json`. Returns `undefined` when the
78
+ * file is missing or malformed — callers fall back to the structural BIO mask only.
79
+ */
80
+ export declare function readCrfTransitions(crfPath: string | undefined): CrfTransitions | undefined;
39
81
  //# sourceMappingURL=weights.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"weights.d.ts","sourceRoot":"","sources":["../weights.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAQH,MAAM,WAAW,kBAAkB;IAClC,wFAAwF;IACxF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,iGAAiG;IACjG,MAAM,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,eAAe,CAoCxE"}
1
+ {"version":3,"file":"weights.d.ts","sourceRoot":"","sources":["../weights.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAQH,MAAM,WAAW,kBAAkB;IAClC,wFAAwF;IACxF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,iGAAiG;IACjG,MAAM,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,eAAe,CAkDxE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAwBxG;AAED,MAAM,WAAW,cAAc;IAC9B,WAAW,EAAE,MAAM,EAAE,EAAE,CAAA;IACvB,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,cAAc,EAAE,MAAM,EAAE,CAAA;CACxB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,GAAG,SAAS,CA0B1F"}
package/out/weights.js CHANGED
@@ -21,7 +21,7 @@
21
21
  * The resolver checks for both files and throws a single actionable error when neither is findable,
22
22
  * naming all the paths it tried.
23
23
  */
24
- import { existsSync } from "node:fs";
24
+ import { existsSync, readFileSync } from "node:fs";
25
25
  import { createRequire } from "node:module";
26
26
  import { dirname, resolve } from "node:path";
27
27
  const req = createRequire(import.meta.url);
@@ -32,9 +32,17 @@ export function resolveWeights(opts) {
32
32
  throw new Error(`Explicit modelPath does not exist: ${opts.modelPath}`);
33
33
  if (!existsSync(opts.tokenizerPath))
34
34
  throw new Error(`Explicit tokenizerPath does not exist: ${opts.tokenizerPath}`);
35
- return { modelPath: opts.modelPath, tokenizerPath: opts.tokenizerPath, source: "explicit" };
35
+ // Resolve a model-card for the label vocab: explicit opt first, else one co-located with the
36
+ // model. Omitting it makes the classifier fall back to STAGE2_BIO_LABELS, which mis-decodes a
37
+ // STAGE3 (33-label) checkpoint into empty parses — the trap that broke eval-matrix --model-path.
38
+ const coLocatedCard = resolve(dirname(opts.modelPath), "model-card.json");
39
+ const modelCardPath = opts.modelCardPath ?? (existsSync(coLocatedCard) ? coLocatedCard : undefined);
40
+ return { modelPath: opts.modelPath, tokenizerPath: opts.tokenizerPath, modelCardPath, source: "explicit" };
36
41
  }
37
- const locale = opts.locale ?? "en-us";
42
+ // Package names follow the all-lowercase BCP-47 convention (`neural-weights-en-us`,
43
+ // `neural-weights-fr-fr`). The CLI's locale validation accepts canonical `en-US` / `fr-FR`
44
+ // casing, so we normalize here rather than at the callsite.
45
+ const locale = (opts.locale ?? "en-us").toLowerCase();
38
46
  const packageName = `@mailwoman/neural-weights-${locale}`;
39
47
  let packageDir;
40
48
  try {
@@ -54,6 +62,86 @@ export function resolveWeights(opts) {
54
62
  `Run \`scripts/link-dev-weights.sh\` inside the package to symlink dev weights, ` +
55
63
  `or pass --model + --tokenizer with explicit paths.`);
56
64
  }
57
- return { modelPath, tokenizerPath, source: `package:${packageName}` };
65
+ const modelCardCandidate = resolve(packageDir, "model-card.json");
66
+ const modelCardPath = existsSync(modelCardCandidate) ? modelCardCandidate : undefined;
67
+ const crfCandidate = resolve(packageDir, "crf-transitions.json");
68
+ const crfTransitionsPath = existsSync(crfCandidate) ? crfCandidate : undefined;
69
+ return { modelPath, tokenizerPath, modelCardPath, crfTransitionsPath, source: `package:${packageName}` };
70
+ }
71
+ /**
72
+ * Read the `labels` array from a `model-card.json` file. Returns `undefined` when the file is
73
+ * missing, unreadable, malformed, or has no `labels` field — callers should fall back to their
74
+ * compile-time default in that case (the loader contract: the JS-side default tracks the most
75
+ * recent shipped stage, so a card without `labels` is always a pre-v0.4.0 card whose label vocab
76
+ * matches that default by construction).
77
+ *
78
+ * Validates shape: must be a non-empty array of strings. Throws on a present-but-malformed `labels`
79
+ * field — a card that emits e.g. `labels: 21` rather than `labels: [...]` is a corrupted artifact
80
+ * and should be loud, not silently re-defaulted.
81
+ */
82
+ export function readLabelsFromModelCard(modelCardPath) {
83
+ if (!modelCardPath || !existsSync(modelCardPath))
84
+ return undefined;
85
+ let raw;
86
+ try {
87
+ raw = readFileSync(modelCardPath, "utf8");
88
+ }
89
+ catch {
90
+ return undefined;
91
+ }
92
+ let parsed;
93
+ try {
94
+ parsed = JSON.parse(raw);
95
+ }
96
+ catch {
97
+ return undefined;
98
+ }
99
+ if (typeof parsed !== "object" || parsed === null)
100
+ return undefined;
101
+ const labels = parsed.labels;
102
+ if (labels === undefined)
103
+ return undefined;
104
+ if (!Array.isArray(labels) || labels.length === 0 || !labels.every((l) => typeof l === "string")) {
105
+ throw new Error(`model-card.json at ${modelCardPath} has a malformed \`labels\` field — ` +
106
+ `expected a non-empty array of strings, got ${JSON.stringify(labels)}.`);
107
+ }
108
+ return Object.freeze(labels.slice());
109
+ }
110
+ /**
111
+ * Read learned CRF transition parameters from `crf-transitions.json`. Returns `undefined` when the
112
+ * file is missing or malformed — callers fall back to the structural BIO mask only.
113
+ */
114
+ export function readCrfTransitions(crfPath) {
115
+ if (!crfPath || !existsSync(crfPath))
116
+ return undefined;
117
+ let raw;
118
+ try {
119
+ raw = readFileSync(crfPath, "utf8");
120
+ }
121
+ catch {
122
+ return undefined;
123
+ }
124
+ let parsed;
125
+ try {
126
+ parsed = JSON.parse(raw);
127
+ }
128
+ catch {
129
+ return undefined;
130
+ }
131
+ if (typeof parsed !== "object" || parsed === null)
132
+ return undefined;
133
+ const obj = parsed;
134
+ const transitions = obj.transitions;
135
+ const start = obj.start_transitions;
136
+ const end = obj.end_transitions;
137
+ if (!Array.isArray(transitions) || !Array.isArray(start) || !Array.isArray(end))
138
+ return undefined;
139
+ if (transitions.length === 0 || start.length === 0 || end.length === 0)
140
+ return undefined;
141
+ return {
142
+ transitions: transitions,
143
+ startTransitions: start,
144
+ endTransitions: end,
145
+ };
58
146
  }
59
147
  //# sourceMappingURL=weights.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"weights.js","sourceRoot":"","sources":["../weights.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAE5C,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAkB1C,MAAM,UAAU,cAAc,CAAC,IAAwB;IACtD,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QAC1C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;QACxG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,IAAI,CAAC,aAAa,EAAE,CAAC,CAAA;QACpH,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,CAAA;IAC5F,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,OAAO,CAAA;IACrC,MAAM,WAAW,GAAG,6BAA6B,MAAM,EAAE,CAAA;IACzD,IAAI,UAAkB,CAAA;IACtB,IAAI,CAAC;QACJ,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,WAAW,eAAe,CAAC,CAAA;QAC9D,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IAClC,CAAC;IAAC,MAAM,CAAC;QACR,MAAM,IAAI,KAAK,CACd,qBAAqB,WAAW,iCAAiC,WAAW,IAAI;YAC/E,oDAAoD,CACrD,CAAA;IACF,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,CAAA;IACrE,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,OAAO,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAA;IAClF,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAA;IAEpC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CACd,mBAAmB,WAAW,gBAAgB,UAAU,gCAAgC;YACvF,aAAa,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI;YACnC,iFAAiF;YACjF,oDAAoD,CACrD,CAAA;IACF,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,WAAW,WAAW,EAAE,EAAE,CAAA;AACtE,CAAC"}
1
+ {"version":3,"file":"weights.js","sourceRoot":"","sources":["../weights.ts"],"names":[],"mappings":"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"}