@mailwoman/neural 4.9.0 → 4.11.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 +102 -0
- package/out/browser.d.ts +1 -0
- package/out/browser.d.ts.map +1 -1
- package/out/browser.js +5 -0
- package/out/browser.js.map +1 -1
- package/out/case-normalize.d.ts +34 -0
- package/out/case-normalize.d.ts.map +1 -0
- package/out/case-normalize.js +50 -0
- package/out/case-normalize.js.map +1 -0
- package/out/classifier.d.ts +24 -2
- package/out/classifier.d.ts.map +1 -1
- package/out/classifier.js +123 -23
- package/out/classifier.js.map +1 -1
- package/out/index.d.ts +2 -0
- package/out/index.d.ts.map +1 -1
- package/out/index.js +2 -0
- package/out/index.js.map +1 -1
- package/out/onnx-runner.d.ts +7 -0
- package/out/onnx-runner.d.ts.map +1 -1
- package/out/onnx-runner.js +10 -0
- package/out/onnx-runner.js.map +1 -1
- package/out/postcode-repair.d.ts +17 -0
- package/out/postcode-repair.d.ts.map +1 -1
- package/out/postcode-repair.js +48 -0
- package/out/postcode-repair.js.map +1 -1
- package/out/scorer.d.ts +105 -0
- package/out/scorer.d.ts.map +1 -0
- package/out/scorer.js +286 -0
- package/out/scorer.js.map +1 -0
- package/out/soft-features.d.ts +67 -0
- package/out/soft-features.d.ts.map +1 -0
- package/out/soft-features.js +49 -0
- package/out/soft-features.js.map +1 -0
- package/out/weights.d.ts +109 -0
- package/out/weights.d.ts.map +1 -1
- package/out/weights.js +157 -1
- package/out/weights.js.map +1 -1
- package/out/word-consistency.d.ts +60 -0
- package/out/word-consistency.d.ts.map +1 -0
- package/out/word-consistency.js +140 -0
- package/out/word-consistency.js.map +1 -0
- package/package.json +5 -3
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* The SOFT-FEATURE channels (#718) — the per-piece anchor + gazetteer clues the model conditions on
|
|
7
|
+
* alongside `input_ids`. This module is the single, PURE, browser-safe home for the channel
|
|
8
|
+
* choreography that used to live inline in `NeuralAddressClassifier.#decode`: build the postcode
|
|
9
|
+
* anchor, build the gazetteer clue, and (when paired with the matching train-time half) suppress
|
|
10
|
+
* the gazetteer clue adjacent to a postcode-anchor hit.
|
|
11
|
+
*
|
|
12
|
+
* It is the LOAD-BEARING contract surface for the ProductionScorer (#718): the scorer asserts which
|
|
13
|
+
* channels are fed, this function decides HOW they are fed. Keeping it a pure function means both
|
|
14
|
+
* the classifier and any harness build features identically — there is exactly one choreography.
|
|
15
|
+
*
|
|
16
|
+
* No `fs`, no Node builtins: the caller hands in the already-parsed lookup/lexicon (mirrors
|
|
17
|
+
* `anchor-inference.ts` / `gazetteer-inference.ts`, which are themselves pure for the same
|
|
18
|
+
* reason).
|
|
19
|
+
*/
|
|
20
|
+
import { type AnchorLookup } from "./anchor-inference.js";
|
|
21
|
+
import { type GazetteerLexicon } from "./gazetteer-inference.js";
|
|
22
|
+
import type { TokenizedPiece } from "./tokenizer.js";
|
|
23
|
+
/** A built soft-feature channel: per-piece feature rows + per-piece confidence. */
|
|
24
|
+
export interface SoftFeatureChannel {
|
|
25
|
+
features: number[][];
|
|
26
|
+
confidence: number[];
|
|
27
|
+
}
|
|
28
|
+
/** The soft-feature channels fed to the runner. Each is present only when its source is configured. */
|
|
29
|
+
export interface SoftFeatures {
|
|
30
|
+
/** Postcode-anchor channel (#239/#240) — present iff `postcodeAnchorLookup` was supplied. */
|
|
31
|
+
anchor?: SoftFeatureChannel;
|
|
32
|
+
/**
|
|
33
|
+
* Gazetteer-anchor channel (#464) — present iff `gazetteerLexicon` was supplied. Already
|
|
34
|
+
* choreographed: when `suppressGazetteerNearPostcode` is set AND an anchor channel exists, the
|
|
35
|
+
* clue is zeroed adjacent to postcode-anchor hits before it's returned here.
|
|
36
|
+
*/
|
|
37
|
+
gazetteer?: SoftFeatureChannel;
|
|
38
|
+
}
|
|
39
|
+
/** Sources + choreography for {@link buildSoftFeatures}. Mirrors the classifier's config fields. */
|
|
40
|
+
export interface SoftFeatureSources {
|
|
41
|
+
/** Postcode→anchor lookup (#239/#240). Omit to skip the anchor channel. */
|
|
42
|
+
postcodeAnchorLookup?: AnchorLookup;
|
|
43
|
+
/** Gazetteer-anchor lexicon (#464). Omit to skip the gazetteer channel. */
|
|
44
|
+
gazetteerLexicon?: GazetteerLexicon;
|
|
45
|
+
/**
|
|
46
|
+
* Channel choreography (#464, v0.9.13 postcode fix): zero the gazetteer clue on pieces adjacent
|
|
47
|
+
* to a postcode-anchor hit. Needs BOTH a `gazetteerLexicon` and a `postcodeAnchorLookup` to take
|
|
48
|
+
* effect (the suppression is keyed off the anchor's confidence). PAIRING IS LOAD-BEARING — enable
|
|
49
|
+
* this IFF the model was trained with the matching train-time choreography. See
|
|
50
|
+
* `suppressGazetteerNearPostcode` in `gazetteer-inference.ts`.
|
|
51
|
+
*/
|
|
52
|
+
suppressGazetteerNearPostcode?: boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Build the soft-feature channels for `text`/`pieces` from the configured sources — the EXACT
|
|
56
|
+
* choreography previously inlined in `NeuralAddressClassifier.#decode`:
|
|
57
|
+
*
|
|
58
|
+
* 1. Anchor channel from `postcodeAnchorLookup` (no-op when unset).
|
|
59
|
+
* 2. Gazetteer channel from `gazetteerLexicon` (no-op when unset).
|
|
60
|
+
* 3. If both channels exist AND `suppressGazetteerNearPostcode`, zero the gazetteer clue adjacent to
|
|
61
|
+
* postcode-anchor hits.
|
|
62
|
+
*
|
|
63
|
+
* Pure + byte-stable: the returned channels are identical to the pre-#718 inline path, so wiring
|
|
64
|
+
* this into `#decode` is a behavior-preserving refactor.
|
|
65
|
+
*/
|
|
66
|
+
export declare function buildSoftFeatures(text: string, pieces: ReadonlyArray<TokenizedPiece>, sources: SoftFeatureSources): SoftFeatures;
|
|
67
|
+
//# sourceMappingURL=soft-features.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"soft-features.d.ts","sourceRoot":"","sources":["../soft-features.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAC9E,OAAO,EAAyD,KAAK,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AACvH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEpD,mFAAmF;AACnF,MAAM,WAAW,kBAAkB;IAClC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAA;IACpB,UAAU,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,uGAAuG;AACvG,MAAM,WAAW,YAAY;IAC5B,6FAA6F;IAC7F,MAAM,CAAC,EAAE,kBAAkB,CAAA;IAC3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,kBAAkB,CAAA;CAC9B;AAED,oGAAoG;AACpG,MAAM,WAAW,kBAAkB;IAClC,2EAA2E;IAC3E,oBAAoB,CAAC,EAAE,YAAY,CAAA;IACnC,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC;;;;;;OAMG;IACH,6BAA6B,CAAC,EAAE,OAAO,CAAA;CACvC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAChC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,CAAC,cAAc,CAAC,EACrC,OAAO,EAAE,kBAAkB,GACzB,YAAY,CAgBd"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* The SOFT-FEATURE channels (#718) — the per-piece anchor + gazetteer clues the model conditions on
|
|
7
|
+
* alongside `input_ids`. This module is the single, PURE, browser-safe home for the channel
|
|
8
|
+
* choreography that used to live inline in `NeuralAddressClassifier.#decode`: build the postcode
|
|
9
|
+
* anchor, build the gazetteer clue, and (when paired with the matching train-time half) suppress
|
|
10
|
+
* the gazetteer clue adjacent to a postcode-anchor hit.
|
|
11
|
+
*
|
|
12
|
+
* It is the LOAD-BEARING contract surface for the ProductionScorer (#718): the scorer asserts which
|
|
13
|
+
* channels are fed, this function decides HOW they are fed. Keeping it a pure function means both
|
|
14
|
+
* the classifier and any harness build features identically — there is exactly one choreography.
|
|
15
|
+
*
|
|
16
|
+
* No `fs`, no Node builtins: the caller hands in the already-parsed lookup/lexicon (mirrors
|
|
17
|
+
* `anchor-inference.ts` / `gazetteer-inference.ts`, which are themselves pure for the same
|
|
18
|
+
* reason).
|
|
19
|
+
*/
|
|
20
|
+
import { buildAnchorFeatures } from "./anchor-inference.js";
|
|
21
|
+
import { buildGazetteerFeatures, suppressGazetteerNearPostcode } from "./gazetteer-inference.js";
|
|
22
|
+
/**
|
|
23
|
+
* Build the soft-feature channels for `text`/`pieces` from the configured sources — the EXACT
|
|
24
|
+
* choreography previously inlined in `NeuralAddressClassifier.#decode`:
|
|
25
|
+
*
|
|
26
|
+
* 1. Anchor channel from `postcodeAnchorLookup` (no-op when unset).
|
|
27
|
+
* 2. Gazetteer channel from `gazetteerLexicon` (no-op when unset).
|
|
28
|
+
* 3. If both channels exist AND `suppressGazetteerNearPostcode`, zero the gazetteer clue adjacent to
|
|
29
|
+
* postcode-anchor hits.
|
|
30
|
+
*
|
|
31
|
+
* Pure + byte-stable: the returned channels are identical to the pre-#718 inline path, so wiring
|
|
32
|
+
* this into `#decode` is a behavior-preserving refactor.
|
|
33
|
+
*/
|
|
34
|
+
export function buildSoftFeatures(text, pieces, sources) {
|
|
35
|
+
const anchor = sources.postcodeAnchorLookup
|
|
36
|
+
? buildAnchorFeatures(text, pieces, sources.postcodeAnchorLookup)
|
|
37
|
+
: undefined;
|
|
38
|
+
const gazetteer = sources.gazetteerLexicon
|
|
39
|
+
? buildGazetteerFeatures(text, pieces, sources.gazetteerLexicon)
|
|
40
|
+
: undefined;
|
|
41
|
+
const gazFed = gazetteer && anchor && sources.suppressGazetteerNearPostcode
|
|
42
|
+
? suppressGazetteerNearPostcode(gazetteer, anchor.confidence)
|
|
43
|
+
: gazetteer;
|
|
44
|
+
return {
|
|
45
|
+
...(anchor ? { anchor } : {}),
|
|
46
|
+
...(gazFed ? { gazetteer: gazFed } : {}),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=soft-features.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"soft-features.js","sourceRoot":"","sources":["../soft-features.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,mBAAmB,EAAqB,MAAM,uBAAuB,CAAA;AAC9E,OAAO,EAAE,sBAAsB,EAAE,6BAA6B,EAAyB,MAAM,0BAA0B,CAAA;AAqCvH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,iBAAiB,CAChC,IAAY,EACZ,MAAqC,EACrC,OAA2B;IAE3B,MAAM,MAAM,GAAG,OAAO,CAAC,oBAAoB;QAC1C,CAAC,CAAC,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,oBAAoB,CAAC;QACjE,CAAC,CAAC,SAAS,CAAA;IACZ,MAAM,SAAS,GAAG,OAAO,CAAC,gBAAgB;QACzC,CAAC,CAAC,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,gBAAgB,CAAC;QAChE,CAAC,CAAC,SAAS,CAAA;IACZ,MAAM,MAAM,GACX,SAAS,IAAI,MAAM,IAAI,OAAO,CAAC,6BAA6B;QAC3D,CAAC,CAAC,6BAA6B,CAAC,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC;QAC7D,CAAC,CAAC,SAAS,CAAA;IAEb,OAAO;QACN,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACxC,CAAA;AACF,CAAC"}
|
package/out/weights.d.ts
CHANGED
|
@@ -36,6 +36,12 @@ export interface ResolveWeightsOpts {
|
|
|
36
36
|
* checkpoint via explicit paths.
|
|
37
37
|
*/
|
|
38
38
|
modelCardPath?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Serving tier (#718 D1). `"server"` (default) = anchor + gazetteer channels; `"pocket"` =
|
|
41
|
+
* anchor-only (skip the gazetteer lexicon even when shipped). Selects which soft-feature sibling
|
|
42
|
+
* artifacts {@link resolveWeights} surfaces — the loader feeds only the resolved channels.
|
|
43
|
+
*/
|
|
44
|
+
tier?: "server" | "pocket";
|
|
39
45
|
}
|
|
40
46
|
export interface ResolvedWeights {
|
|
41
47
|
modelPath: string;
|
|
@@ -52,6 +58,26 @@ export interface ResolvedWeights {
|
|
|
52
58
|
* exist (pre-v0.6.0 bundles or CE-only training).
|
|
53
59
|
*/
|
|
54
60
|
crfTransitionsPath?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Path to the postcode→anchor source shipped beside the resolved model (#718 D1) — the soft-feed
|
|
63
|
+
* `loadFromWeights` reads to feed the anchor channel without a callsite change. Prefer the
|
|
64
|
+
* compact PCB1 binary (`postcode-<cc>.bin`, decoded via
|
|
65
|
+
* `PostcodeBinaryResolver.toAnchorLookup()`), else a JSON anchor lookup (`anchor-lookup.json`,
|
|
66
|
+
* parsed via `parseAnchorLookup`). `undefined` when the package ships neither (a plain/pre-#718
|
|
67
|
+
* bundle) — the loader then runs anchor-OFF. The `binary` flag tells the loader which parser to
|
|
68
|
+
* use.
|
|
69
|
+
*/
|
|
70
|
+
anchorLookupPath?: {
|
|
71
|
+
path: string;
|
|
72
|
+
binary: boolean;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Path to the gazetteer-anchor lexicon (`anchor-lexicon-v1.json`, #464) shipped beside the
|
|
76
|
+
* resolved model. `undefined` when the package doesn't ship it, OR when `opts.tier === "pocket"`
|
|
77
|
+
* (pocket is anchor-only — the gazetteer channel is deliberately skipped). Read by the
|
|
78
|
+
* `loadFromWeights` soft-feed via `parseGazetteerLexicon`.
|
|
79
|
+
*/
|
|
80
|
+
gazetteerLexiconPath?: string;
|
|
55
81
|
/** "explicit" if both paths came from opts; "package:<name>" if resolved via require.resolve. */
|
|
56
82
|
source: string;
|
|
57
83
|
}
|
|
@@ -68,6 +94,89 @@ export declare function resolveWeights(opts: ResolveWeightsOpts): ResolvedWeight
|
|
|
68
94
|
* and should be loud, not silently re-defaulted.
|
|
69
95
|
*/
|
|
70
96
|
export declare function readLabelsFromModelCard(modelCardPath: string | undefined): readonly string[] | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* The structured `requires` block of a `model-card.json` (#718) — the declared SHIP-CONFIG the
|
|
99
|
+
* model was trained against. The ProductionScorer reads this and FAILS CLOSED when a declared
|
|
100
|
+
* channel isn't actually fed (silent OOD is the #566/#685 trap). Each channel is optional; a
|
|
101
|
+
* missing channel means "not declared" (treated as not-required).
|
|
102
|
+
*/
|
|
103
|
+
export interface RequiredChannels {
|
|
104
|
+
/** Postcode-anchor channel (#239/#240). */
|
|
105
|
+
anchor?: {
|
|
106
|
+
required: boolean;
|
|
107
|
+
};
|
|
108
|
+
/** Gazetteer-anchor channel (#464). */
|
|
109
|
+
gazetteer?: {
|
|
110
|
+
required: boolean;
|
|
111
|
+
};
|
|
112
|
+
/** Address-system conventions (#511 Tier A). `mode` mirrors `ParseOpts.addressSystemConventions`. */
|
|
113
|
+
conventions?: {
|
|
114
|
+
required: boolean;
|
|
115
|
+
mode?: "auto" | string;
|
|
116
|
+
};
|
|
117
|
+
/** Punctuation-gap span bridge (v4.4.0 corrective). */
|
|
118
|
+
bridge?: {
|
|
119
|
+
required: boolean;
|
|
120
|
+
};
|
|
121
|
+
/** Near-postcode gazetteer choreography (#464, v0.9.13). */
|
|
122
|
+
suppress_gazetteer_near_postcode?: boolean;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Read the structured `requires` block from a `model-card.json` (#718). DEFENSIVE: returns
|
|
126
|
+
* `undefined` when the card is absent, unreadable, or has no `requires` field (callers then INFER
|
|
127
|
+
* the required channels from the ONNX graph — see `inferRequiredChannelsFromInputs`). Throws ONLY
|
|
128
|
+
* when the field is PRESENT but corrupt (not an object, or a channel entry with a non-boolean
|
|
129
|
+
* `required`) — a malformed declared contract is a loud artifact bug, not a silent re-default.
|
|
130
|
+
*/
|
|
131
|
+
export declare function readRequiredChannels(modelCardPath: string | undefined): RequiredChannels | undefined;
|
|
132
|
+
/**
|
|
133
|
+
* Back-compat inference of the required soft-feature channels from an ONNX model's declared input
|
|
134
|
+
* names (#718). A model that exports `anchor_features` / `gazetteer_features` declared those
|
|
135
|
+
* channels mandatory at train time — feeding zeros is the channel-off identity, but a model TRAINED
|
|
136
|
+
* with the channel is OOD when scored without it. Cards without a `requires` block (every pre-#718
|
|
137
|
+
* bundle) route through here so the fail-closed guard still protects them. Conventions/bridge are
|
|
138
|
+
* NOT graph-observable (no dedicated input), so they're left undeclared here — only the card
|
|
139
|
+
* declares them.
|
|
140
|
+
*/
|
|
141
|
+
export declare function inferRequiredChannelsFromInputs(inputNames: readonly string[]): RequiredChannels;
|
|
142
|
+
/**
|
|
143
|
+
* One tag's certified capability under a (tier × address-system) cell of the capability manifest
|
|
144
|
+
* (#718/#719). `maskOffF1` is the model's measured per-tag exact-match F1 with the conventions mask
|
|
145
|
+
* OFF; `maskOnF1` is the same with the mask ON — recorded ONLY for tags some codex `forbiddenTags`
|
|
146
|
+
* row suppresses, because that's the only place the loader's delta-gate consults it.
|
|
147
|
+
*/
|
|
148
|
+
export interface TagCapability {
|
|
149
|
+
/** Measured per-tag F1 (percent) with the conventions mask OFF — the model's real capability. */
|
|
150
|
+
maskOffF1: number;
|
|
151
|
+
/** Measured per-tag F1 (percent) with the mask ON. Present only for codex-forbidden tags. */
|
|
152
|
+
maskOnF1?: number;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* The `capabilities` block of a `model-card.json` (#718/#719): per serving TIER (`server` =
|
|
156
|
+
* anchor+gazetteer; `pocket` = anchor-only) × per codex address-system × per tag, the model's
|
|
157
|
+
* certified per-tag capability. The `createScorer` loader reads this to FAIL CLOSED when a
|
|
158
|
+
* conventions mask would forbid a tag the model is certified to emit — the structural fix that
|
|
159
|
+
* makes the D2/#719 bug-class (a mask destroying a demonstrated capability) impossible.
|
|
160
|
+
*
|
|
161
|
+
* Shape: `capabilities[tier][system][tag] = { maskOffF1, maskOnF1? }`. A `$comment` provenance key
|
|
162
|
+
* may sit alongside the tier keys and is ignored by readers.
|
|
163
|
+
*/
|
|
164
|
+
export type CapabilityManifest = Record<string, Record<string, Record<string, TagCapability>>>;
|
|
165
|
+
/**
|
|
166
|
+
* Read the `capabilities` block from a `model-card.json` (#718/#719). DEFENSIVE, mirroring
|
|
167
|
+
* `readRequiredChannels`: returns `undefined` when the card is absent, unreadable, or has no
|
|
168
|
+
* `capabilities` field (a pre-#718 card → the loader's delta-gate is skipped, back-compat). Throws
|
|
169
|
+
* ONLY when the field is PRESENT but not an object — a corrupt declared contract is a loud artifact
|
|
170
|
+
* bug, not a silent skip. Tier/system/tag sub-shapes are read leniently (a malformed cell simply
|
|
171
|
+
* yields no capability claim — `undefined` from `lookupTagCapability`).
|
|
172
|
+
*/
|
|
173
|
+
export declare function readCapabilityManifest(modelCardPath: string | undefined): CapabilityManifest | undefined;
|
|
174
|
+
/**
|
|
175
|
+
* Resolve `capabilities[tier][system][tag]` to a `TagCapability`, returning `undefined` for any
|
|
176
|
+
* missing/malformed cell (a tag the model is NOT certified for — the loader treats that as legal:
|
|
177
|
+
* the model can't emit it, so a mask can't destroy it). Skips the `$comment` provenance key.
|
|
178
|
+
*/
|
|
179
|
+
export declare function lookupTagCapability(manifest: CapabilityManifest | undefined, tier: string, system: string, tag: string): TagCapability | undefined;
|
|
71
180
|
export interface CrfTransitions {
|
|
72
181
|
transitions: number[][];
|
|
73
182
|
startTransitions: number[];
|
package/out/weights.d.ts.map
CHANGED
|
@@ -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;IACtB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"weights.d.ts","sourceRoot":"","sources":["../weights.ts"],"names":[],"mappings":"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"}
|
package/out/weights.js
CHANGED
|
@@ -66,7 +66,42 @@ export function resolveWeights(opts) {
|
|
|
66
66
|
const modelCardPath = existsSync(modelCardCandidate) ? modelCardCandidate : undefined;
|
|
67
67
|
const crfCandidate = resolve(packageDir, "crf-transitions.json");
|
|
68
68
|
const crfTransitionsPath = existsSync(crfCandidate) ? crfCandidate : undefined;
|
|
69
|
-
|
|
69
|
+
// Soft-feature sibling artifacts (#718 D1): the anchor + gazetteer sources the package ships so
|
|
70
|
+
// `loadFromWeights` can feed the channels the model was trained against — without a callsite
|
|
71
|
+
// change. Resolved package-dir-relative via the same `existsSync → undefined` pattern as the CRF
|
|
72
|
+
// transitions above. The locale tag's region subtag (`en-us` → `us`) names the PCB1 binary.
|
|
73
|
+
const country = locale.split("-")[1] ?? "";
|
|
74
|
+
const anchorLookupPath = resolveAnchorLookupSibling(packageDir, country);
|
|
75
|
+
// Tier `"pocket"` is anchor-only — never surface the gazetteer lexicon (the loader then skips it).
|
|
76
|
+
const gazetteerCandidate = resolve(packageDir, "anchor-lexicon-v1.json");
|
|
77
|
+
const gazetteerLexiconPath = opts.tier === "pocket" ? undefined : existsSync(gazetteerCandidate) ? gazetteerCandidate : undefined;
|
|
78
|
+
return {
|
|
79
|
+
modelPath,
|
|
80
|
+
tokenizerPath,
|
|
81
|
+
modelCardPath,
|
|
82
|
+
crfTransitionsPath,
|
|
83
|
+
...(anchorLookupPath ? { anchorLookupPath } : {}),
|
|
84
|
+
...(gazetteerLexiconPath ? { gazetteerLexiconPath } : {}),
|
|
85
|
+
source: `package:${packageName}`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Locate the package's postcode→anchor source for the soft-feed (#718 D1), preferring the compact
|
|
90
|
+
* PCB1 binary (`postcode-<cc>.bin`, ~0.66 MB) over the much larger JSON lookup
|
|
91
|
+
* (`anchor-lookup.json`, the 3.2 MB pilot dump). Returns the path + a `binary` flag so the loader
|
|
92
|
+
* picks the right parser (`PostcodeBinaryResolver.toAnchorLookup()` vs `parseAnchorLookup`).
|
|
93
|
+
* `undefined` when neither ships.
|
|
94
|
+
*/
|
|
95
|
+
function resolveAnchorLookupSibling(packageDir, country) {
|
|
96
|
+
if (country) {
|
|
97
|
+
const binary = resolve(packageDir, `postcode-${country}.bin`);
|
|
98
|
+
if (existsSync(binary))
|
|
99
|
+
return { path: binary, binary: true };
|
|
100
|
+
}
|
|
101
|
+
const json = resolve(packageDir, "anchor-lookup.json");
|
|
102
|
+
if (existsSync(json))
|
|
103
|
+
return { path: json, binary: false };
|
|
104
|
+
return undefined;
|
|
70
105
|
}
|
|
71
106
|
/**
|
|
72
107
|
* Read the `labels` array from a `model-card.json` file. Returns `undefined` when the file is
|
|
@@ -107,6 +142,127 @@ export function readLabelsFromModelCard(modelCardPath) {
|
|
|
107
142
|
}
|
|
108
143
|
return Object.freeze(labels.slice());
|
|
109
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Read the structured `requires` block from a `model-card.json` (#718). DEFENSIVE: returns
|
|
147
|
+
* `undefined` when the card is absent, unreadable, or has no `requires` field (callers then INFER
|
|
148
|
+
* the required channels from the ONNX graph — see `inferRequiredChannelsFromInputs`). Throws ONLY
|
|
149
|
+
* when the field is PRESENT but corrupt (not an object, or a channel entry with a non-boolean
|
|
150
|
+
* `required`) — a malformed declared contract is a loud artifact bug, not a silent re-default.
|
|
151
|
+
*/
|
|
152
|
+
export function readRequiredChannels(modelCardPath) {
|
|
153
|
+
if (!modelCardPath || !existsSync(modelCardPath))
|
|
154
|
+
return undefined;
|
|
155
|
+
let raw;
|
|
156
|
+
try {
|
|
157
|
+
raw = readFileSync(modelCardPath, "utf8");
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
let parsed;
|
|
163
|
+
try {
|
|
164
|
+
parsed = JSON.parse(raw);
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
170
|
+
return undefined;
|
|
171
|
+
const requires = parsed.requires;
|
|
172
|
+
if (requires === undefined)
|
|
173
|
+
return undefined;
|
|
174
|
+
if (typeof requires !== "object" || requires === null || Array.isArray(requires)) {
|
|
175
|
+
throw new Error(`model-card.json at ${modelCardPath} has a malformed \`requires\` field — ` +
|
|
176
|
+
`expected an object, got ${JSON.stringify(requires)}.`);
|
|
177
|
+
}
|
|
178
|
+
const obj = requires;
|
|
179
|
+
// Channel entries must be `{ required: boolean, ... }`; a present-but-shapeless entry is corrupt.
|
|
180
|
+
for (const channel of ["anchor", "gazetteer", "conventions", "bridge"]) {
|
|
181
|
+
const entry = obj[channel];
|
|
182
|
+
if (entry === undefined)
|
|
183
|
+
continue;
|
|
184
|
+
if (typeof entry !== "object" ||
|
|
185
|
+
entry === null ||
|
|
186
|
+
typeof entry.required !== "boolean") {
|
|
187
|
+
throw new Error(`model-card.json at ${modelCardPath} has a malformed \`requires.${channel}\` entry — ` +
|
|
188
|
+
`expected { required: boolean }, got ${JSON.stringify(entry)}.`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (obj.suppress_gazetteer_near_postcode !== undefined && typeof obj.suppress_gazetteer_near_postcode !== "boolean") {
|
|
192
|
+
throw new Error(`model-card.json at ${modelCardPath} has a malformed \`requires.suppress_gazetteer_near_postcode\` ` +
|
|
193
|
+
`field — expected a boolean, got ${JSON.stringify(obj.suppress_gazetteer_near_postcode)}.`);
|
|
194
|
+
}
|
|
195
|
+
return requires;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Back-compat inference of the required soft-feature channels from an ONNX model's declared input
|
|
199
|
+
* names (#718). A model that exports `anchor_features` / `gazetteer_features` declared those
|
|
200
|
+
* channels mandatory at train time — feeding zeros is the channel-off identity, but a model TRAINED
|
|
201
|
+
* with the channel is OOD when scored without it. Cards without a `requires` block (every pre-#718
|
|
202
|
+
* bundle) route through here so the fail-closed guard still protects them. Conventions/bridge are
|
|
203
|
+
* NOT graph-observable (no dedicated input), so they're left undeclared here — only the card
|
|
204
|
+
* declares them.
|
|
205
|
+
*/
|
|
206
|
+
export function inferRequiredChannelsFromInputs(inputNames) {
|
|
207
|
+
const names = new Set(inputNames);
|
|
208
|
+
return {
|
|
209
|
+
...(names.has("anchor_features") ? { anchor: { required: true } } : {}),
|
|
210
|
+
...(names.has("gazetteer_features") ? { gazetteer: { required: true } } : {}),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Read the `capabilities` block from a `model-card.json` (#718/#719). DEFENSIVE, mirroring
|
|
215
|
+
* `readRequiredChannels`: returns `undefined` when the card is absent, unreadable, or has no
|
|
216
|
+
* `capabilities` field (a pre-#718 card → the loader's delta-gate is skipped, back-compat). Throws
|
|
217
|
+
* ONLY when the field is PRESENT but not an object — a corrupt declared contract is a loud artifact
|
|
218
|
+
* bug, not a silent skip. Tier/system/tag sub-shapes are read leniently (a malformed cell simply
|
|
219
|
+
* yields no capability claim — `undefined` from `lookupTagCapability`).
|
|
220
|
+
*/
|
|
221
|
+
export function readCapabilityManifest(modelCardPath) {
|
|
222
|
+
if (!modelCardPath || !existsSync(modelCardPath))
|
|
223
|
+
return undefined;
|
|
224
|
+
let raw;
|
|
225
|
+
try {
|
|
226
|
+
raw = readFileSync(modelCardPath, "utf8");
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
let parsed;
|
|
232
|
+
try {
|
|
233
|
+
parsed = JSON.parse(raw);
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return undefined;
|
|
237
|
+
}
|
|
238
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
239
|
+
return undefined;
|
|
240
|
+
const capabilities = parsed.capabilities;
|
|
241
|
+
if (capabilities === undefined)
|
|
242
|
+
return undefined;
|
|
243
|
+
if (typeof capabilities !== "object" || capabilities === null || Array.isArray(capabilities)) {
|
|
244
|
+
throw new Error(`model-card.json at ${modelCardPath} has a malformed \`capabilities\` field — ` +
|
|
245
|
+
`expected an object, got ${JSON.stringify(capabilities)}.`);
|
|
246
|
+
}
|
|
247
|
+
return capabilities;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Resolve `capabilities[tier][system][tag]` to a `TagCapability`, returning `undefined` for any
|
|
251
|
+
* missing/malformed cell (a tag the model is NOT certified for — the loader treats that as legal:
|
|
252
|
+
* the model can't emit it, so a mask can't destroy it). Skips the `$comment` provenance key.
|
|
253
|
+
*/
|
|
254
|
+
export function lookupTagCapability(manifest, tier, system, tag) {
|
|
255
|
+
const tierCell = manifest?.[tier];
|
|
256
|
+
if (!tierCell || typeof tierCell !== "object")
|
|
257
|
+
return undefined;
|
|
258
|
+
const systemCell = tierCell[system];
|
|
259
|
+
if (!systemCell || typeof systemCell !== "object")
|
|
260
|
+
return undefined;
|
|
261
|
+
const cap = systemCell[tag];
|
|
262
|
+
if (!cap || typeof cap !== "object" || typeof cap.maskOffF1 !== "number")
|
|
263
|
+
return undefined;
|
|
264
|
+
return cap;
|
|
265
|
+
}
|
|
110
266
|
/**
|
|
111
267
|
* Read learned CRF transition parameters from `crf-transitions.json`. Returns `undefined` when the
|
|
112
268
|
* file is missing or malformed — callers fall back to the structural BIO mask only.
|
package/out/weights.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"weights.js","sourceRoot":"","sources":["../weights.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAClD,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;
|
|
1
|
+
{"version":3,"file":"weights.js","sourceRoot":"","sources":["../weights.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Per-word BIO tag-consistency repair (#727 + the fr.country / admin-token fragmentation class).
|
|
7
|
+
*
|
|
8
|
+
* The model emits per-PIECE BIO labels. On rows where an admin token is adjacent to a non-latin
|
|
9
|
+
* (byte-fallback) locality, carries diacritics, or is all-caps/reordered, the per-piece labels
|
|
10
|
+
* can DISAGREE within a single word — `VERMONT` → `VER`[B-locality] + `MONT`[B-region], `Lozère`
|
|
11
|
+
* → `Loz`[locality] + `ère`[B-region] — and the span decoder reads that as a tag-change
|
|
12
|
+
* mid-token, fracturing the admin component. The model already KNOWS the word's tag (99.7% of
|
|
13
|
+
* normal rows are unanimous); the defect is the lack of a word-level consistency constraint.
|
|
14
|
+
*
|
|
15
|
+
* Fix (DeepSeek-Pro consult `contested-frag`, 2026-06-19): a SentencePiece word — a `▁`-started
|
|
16
|
+
* piece + its non-`▁` continuations — must carry ONE tag. The tag is chosen by a
|
|
17
|
+
* CONFIDENCE-WEIGHTED vote, NOT first-piece-wins: sum each piece's softmax mass per TAG TYPE
|
|
18
|
+
* (B-X
|
|
19
|
+
*
|
|
20
|
+
* - I-X collapsed; `O` included) across the word, argmax the type, and force `B-<type>` then
|
|
21
|
+
* `I-<type>` (or all `O`). The near-certain `ère`→region then pulls the whole word to region,
|
|
22
|
+
* healing both the fragment and the `Loz` bleed. Operates ONLY within a `▁`-delimited word,
|
|
23
|
+
* so cross-word multi-token names ("Saint Paul" → two words) are untouched and the decoder's
|
|
24
|
+
* existing cross-word merge still joins them.
|
|
25
|
+
*
|
|
26
|
+
* Safety: a word whose pieces already agree is left byte-identical (no change). The vote includes
|
|
27
|
+
* `O`, so a genuinely-`O` word stays `O` (no spurious spans).
|
|
28
|
+
*
|
|
29
|
+
* Gate outcome (2026-06-19, fr-admin-split-gate + per-locale-f1, MAILWOMAN_WORD_CONSISTENCY=1): NOT
|
|
30
|
+
* a clean win, so this ships DEFAULT-OFF. It heals clean-latin fragments (`PRUNIÈRES LOZÈRE
|
|
31
|
+
* FRANCE` → region=`LOZÈRE`), but on OOD byte-soup rows where the per-piece confidence is itself
|
|
32
|
+
* unreliable the vote AMPLIFIES that noise — e.g. `VERMONT, ウェストミンスター` lets the region word
|
|
33
|
+
* absorb the adjacent CJK locality — and it net-regressed street −12.6 on the adversarial golden.
|
|
34
|
+
* The confidence-weighted vote out-votes a stray mis-tagged piece only when the surviving pieces
|
|
35
|
+
* are themselves trustworthy; on byte-fallback pieces that premise breaks. A confidence-gated
|
|
36
|
+
* variant (skip the heal when the word's mean p(bestType) is below a floor, or when any piece is
|
|
37
|
+
* a raw byte-fallback piece) is the path to a clean win — tracked on #727.
|
|
38
|
+
*/
|
|
39
|
+
export interface WordConsistencyResult {
|
|
40
|
+
/** A new per-piece label-index array, word-consistent (input is not mutated). */
|
|
41
|
+
labelIndices: number[];
|
|
42
|
+
/** PieceIndex → mean p(chosen type) across the word, for pieces in a word that was HEALED. */
|
|
43
|
+
healedConfidence: Map<number, number>;
|
|
44
|
+
/** Count of words whose labels were rewritten (0 = byte-identical to the input). */
|
|
45
|
+
healedWords: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Rewrite per-piece label indices so every `▁`-delimited word carries one tag, chosen by a
|
|
49
|
+
* confidence-weighted vote over the post-prior `emissions`. See the module docstring.
|
|
50
|
+
*
|
|
51
|
+
* @param pieces SentencePiece pieces (the `▁`-marked surface is the word-boundary signal).
|
|
52
|
+
* @param emissions Per-piece × per-label scores AFTER all priors/masks (the distribution the argmax
|
|
53
|
+
* would see). Softmaxed per piece for the vote so each piece's confidence carries its weight.
|
|
54
|
+
* @param labels The BIO label vocabulary (index ↔ label).
|
|
55
|
+
* @param labelIndices The current per-piece decision (viterbi path or argmax). Not mutated.
|
|
56
|
+
*/
|
|
57
|
+
export declare function enforceWordConsistency(pieces: ReadonlyArray<{
|
|
58
|
+
piece: string;
|
|
59
|
+
}>, emissions: ReadonlyArray<ReadonlyArray<number>>, labels: readonly string[], labelIndices: readonly number[]): WordConsistencyResult;
|
|
60
|
+
//# sourceMappingURL=word-consistency.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"word-consistency.d.ts","sourceRoot":"","sources":["../word-consistency.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAKH,MAAM,WAAW,qBAAqB;IACrC,iFAAiF;IACjF,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,8FAA8F;IAC9F,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC,oFAAoF;IACpF,WAAW,EAAE,MAAM,CAAA;CACnB;AASD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACrC,MAAM,EAAE,aAAa,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EACxC,SAAS,EAAE,aAAa,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,EAC/C,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,YAAY,EAAE,SAAS,MAAM,EAAE,GAC7B,qBAAqB,CA+EvB"}
|