@mailwoman/neural-web 0.1.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 +66 -0
- package/out/index.d.ts +10 -0
- package/out/index.d.ts.map +1 -0
- package/out/index.js +12 -0
- package/out/index.js.map +1 -0
- package/out/loader.d.ts +110 -0
- package/out/loader.d.ts.map +1 -0
- package/out/loader.js +199 -0
- package/out/loader.js.map +1 -0
- package/out/web-onnx-runner.d.ts +76 -0
- package/out/web-onnx-runner.d.ts.map +1 -0
- package/out/web-onnx-runner.js +188 -0
- package/out/web-onnx-runner.js.map +1 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# @mailwoman/neural-web
|
|
2
|
+
|
|
3
|
+
Browser-side mailwoman neural runtime — drop-in for [`@mailwoman/neural`](https://www.npmjs.com/package/@mailwoman/neural) when targeting a static-asset deploy. Pairs the existing SentencePiece tokenizer + BIO decoder with an [`onnxruntime-web`](https://www.npmjs.com/package/onnxruntime-web) inference path (WebGPU primary, WASM SIMD fallback).
|
|
4
|
+
|
|
5
|
+
Path B of the demo plan — see [sister-software/mailwoman#98](https://github.com/sister-software/mailwoman/issues/98).
|
|
6
|
+
|
|
7
|
+
## Status
|
|
8
|
+
|
|
9
|
+
**v0.1.0 — scaffold + end-to-end smoke test.** `WebOnnxRunner` implements the `NeuralRunner` interface and is composable into `NeuralAddressClassifier` exactly like `OnnxRunner` from `@mailwoman/neural`. Test suite runs the real `@mailwoman/neural-weights-en-us` model through the WASM execution provider in Node — WebGPU is the production-time fast path but isn't testable in Node.
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { loadNeuralClassifierFromUrls } from "@mailwoman/neural-web"
|
|
15
|
+
|
|
16
|
+
const classifier = await loadNeuralClassifierFromUrls({
|
|
17
|
+
modelUrl: "/static/mailwoman/model.onnx",
|
|
18
|
+
tokenizerUrl: "/static/mailwoman/tokenizer.model",
|
|
19
|
+
runner: {
|
|
20
|
+
// Optional. If your bundler doesn't put ort .wasm files in the default location,
|
|
21
|
+
// point this at where you serve them.
|
|
22
|
+
wasmPathsRoot: "/static/ort/",
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const tree = await classifier.parse("123 Main St, Springfield, IL 62704")
|
|
27
|
+
console.log(tree.roots)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
For lower-level control, use `WebOnnxRunner` directly:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { WebOnnxRunner, MailwomanTokenizer, NeuralAddressClassifier } from "@mailwoman/neural-web"
|
|
34
|
+
|
|
35
|
+
const runner = await WebOnnxRunner.fromUrl("/static/mailwoman/model.onnx", { useWebGpu: true })
|
|
36
|
+
const tokenizer = await MailwomanTokenizer.loadFromBase64(/* base64 of tokenizer.model */)
|
|
37
|
+
const classifier = new NeuralAddressClassifier({ tokenizer, runner })
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Execution provider strategy
|
|
41
|
+
|
|
42
|
+
`WebOnnxRunner` attempts WebGPU first (10× faster than WASM on supported devices — Chromium 113+, Safari Tech Preview, hardware-dependent). If the WebGPU probe fails (no adapter, browser doesn't expose it, etc.), it transparently falls back to the WASM execution provider. Set `useWebGpu: false` to skip the probe entirely — useful in test environments where the failure path adds latency.
|
|
43
|
+
|
|
44
|
+
## Bundling
|
|
45
|
+
|
|
46
|
+
This package ships compiled TypeScript only. The `onnxruntime-web` runtime ships its own `.wasm` assets — your bundler needs to serve them. The package's defaults point at a CDN; production deploys typically self-host:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { loadNeuralClassifierFromUrls } from "@mailwoman/neural-web"
|
|
50
|
+
|
|
51
|
+
// Copy node_modules/onnxruntime-web/dist/*.wasm into your /public dir during build,
|
|
52
|
+
// then point the runner at them:
|
|
53
|
+
const classifier = await loadNeuralClassifierFromUrls({
|
|
54
|
+
modelUrl: "/static/mailwoman/model.onnx",
|
|
55
|
+
tokenizerUrl: "/static/mailwoman/tokenizer.model",
|
|
56
|
+
runner: { wasmPathsRoot: "/ort-wasm/" },
|
|
57
|
+
})
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Why not extend `@mailwoman/neural` directly?
|
|
61
|
+
|
|
62
|
+
`@mailwoman/neural` depends on `onnxruntime-node`, which ships native binaries and breaks in a browser bundle. The classifier surface itself is runtime-agnostic — it only needs a `NeuralRunner` (a structural interface with `infer(ids): Promise<InferResult>`). Splitting the runner lets both implementations co-exist without forcing browser bundlers to dead-code-eliminate native code.
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
AGPL-3.0-only.
|
package/out/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*/
|
|
6
|
+
export { defaultGazetteerLexiconUrl, loadNeuralClassifierFromUrls, type LoadFromUrlsOpts } from "./loader.js";
|
|
7
|
+
export { DEFAULT_FIXED_SEQ_LEN, WebOnnxRunner, type WebOnnxRunnerOpts } from "./web-onnx-runner.js";
|
|
8
|
+
export { MailwomanTokenizer, NeuralAddressClassifier, type NeuralAddressClassifierConfig, type NeuralRunner, } from "@mailwoman/neural/browser";
|
|
9
|
+
export type { InferResult } from "@mailwoman/neural/browser";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,0BAA0B,EAAE,4BAA4B,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC7G,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAKnG,OAAO,EACN,kBAAkB,EAClB,uBAAuB,EACvB,KAAK,6BAA6B,EAClC,KAAK,YAAY,GACjB,MAAM,2BAA2B,CAAA;AAClC,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA"}
|
package/out/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*/
|
|
6
|
+
export { defaultGazetteerLexiconUrl, loadNeuralClassifierFromUrls } from "./loader.js";
|
|
7
|
+
export { DEFAULT_FIXED_SEQ_LEN, WebOnnxRunner } from "./web-onnx-runner.js";
|
|
8
|
+
// Re-export the public neural surface so callers don't need both packages on the typed path.
|
|
9
|
+
// Pull from the browser-safe entry — the default entry would drag onnxruntime-node + node:fs
|
|
10
|
+
// into the bundle graph via classifier.ts's transitive imports.
|
|
11
|
+
export { MailwomanTokenizer, NeuralAddressClassifier, } from "@mailwoman/neural/browser";
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
package/out/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,0BAA0B,EAAE,4BAA4B,EAAyB,MAAM,aAAa,CAAA;AAC7G,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAA0B,MAAM,sBAAsB,CAAA;AAEnG,6FAA6F;AAC7F,6FAA6F;AAC7F,gEAAgE;AAChE,OAAO,EACN,kBAAkB,EAClB,uBAAuB,GAGvB,MAAM,2BAA2B,CAAA"}
|
package/out/loader.d.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Browser-side loader that pairs the existing `MailwomanTokenizer` (whose `loadFromBase64` path is
|
|
7
|
+
* already browser-safe — it doesn't touch Node fs) with a fresh `WebOnnxRunner`, and returns a
|
|
8
|
+
* ready-to-use `NeuralAddressClassifier`.
|
|
9
|
+
*
|
|
10
|
+
* V1 strategy: fetch both `model.onnx` and `tokenizer.model` over HTTP from caller-provided URLs
|
|
11
|
+
* (typically pointing at the same static-asset bundle that ships the resolver's slim WOF DB). The
|
|
12
|
+
* neural weights package `@mailwoman/neural-weights-en-us` is the canonical source of those two
|
|
13
|
+
* files; for a static deploy, copy them into the public bundle and pass the resulting URLs.
|
|
14
|
+
*/
|
|
15
|
+
import { NeuralAddressClassifier, type NeuralAddressClassifierConfig } from "@mailwoman/neural/browser";
|
|
16
|
+
import { type WebOnnxRunnerDiagnostics, type WebOnnxRunnerOpts } from "./web-onnx-runner.js";
|
|
17
|
+
export type { WebOnnxRunnerDiagnostics };
|
|
18
|
+
export interface LoadResult {
|
|
19
|
+
classifier: NeuralAddressClassifier;
|
|
20
|
+
diagnostics: WebOnnxRunnerDiagnostics | null;
|
|
21
|
+
/**
|
|
22
|
+
* Labels actually applied to the classifier. `null` when no model-card was provided or its
|
|
23
|
+
* `labels` field was missing — the classifier fell back to its built-in default (Stage 2).
|
|
24
|
+
*/
|
|
25
|
+
labels: readonly string[] | null;
|
|
26
|
+
/**
|
|
27
|
+
* The parsed postcode-anchor lookup (postcode → posterior + centroid), when anchor binaries were
|
|
28
|
+
* loaded. Exposed so consumers (the demo's anchor-centroid map fallback) can reuse the SAME
|
|
29
|
+
* artifact the model channel feeds from — WOF ships placeholder (0,0) for ~22% of US postcodes;
|
|
30
|
+
* this lookup has a real centroid for every covered ZIP.
|
|
31
|
+
*/
|
|
32
|
+
postcodeAnchorLookup?: import("@mailwoman/neural").AnchorLookup;
|
|
33
|
+
}
|
|
34
|
+
export interface LoadFromUrlsOpts {
|
|
35
|
+
/** URL to the ONNX model file (e.g. `/static/mailwoman/model.onnx`). */
|
|
36
|
+
modelUrl: string;
|
|
37
|
+
/** URL to the SentencePiece tokenizer model (e.g. `/static/mailwoman/tokenizer.model`). */
|
|
38
|
+
tokenizerUrl: string;
|
|
39
|
+
/**
|
|
40
|
+
* URL to `model-card.json`. When provided, its `labels` field is threaded into the classifier so
|
|
41
|
+
* post-Stage-2 bundles (33-label Stage 3 and beyond) decode correctly. Skip for legacy bundles
|
|
42
|
+
* whose cards predate the `labels` field — the loader falls back to the built-in Stage 2
|
|
43
|
+
* default.
|
|
44
|
+
*
|
|
45
|
+
* Required for any v0.6.x+ bundle: without it the classifier builds a 21×21 transition mask while
|
|
46
|
+
* the model emits 33 logits and viterbi crashes with "Cannot read properties of undefined".
|
|
47
|
+
*/
|
|
48
|
+
modelCardUrl?: string;
|
|
49
|
+
/** Runner options (WebGPU toggle, fixed sequence length, WASM path override). */
|
|
50
|
+
runner?: WebOnnxRunnerOpts;
|
|
51
|
+
/**
|
|
52
|
+
* URLs to one or more PCB1 postcode binaries (`postcode-<cc>.bin`). For anchor-trained models
|
|
53
|
+
* (#239/#240) these are decoded + merged into the postcode→anchor lookup the classifier feeds at
|
|
54
|
+
* inference, so the demo runs the model with the anchor on. Pass the locales the model handles
|
|
55
|
+
* (e.g. US + DE). Omit for plain models — the runner then feeds the anchor-off identity.
|
|
56
|
+
*/
|
|
57
|
+
postcodeBinaryUrls?: readonly string[];
|
|
58
|
+
/**
|
|
59
|
+
* URL to the gazetteer-anchor lexicon JSON (`anchor-lexicon-v1.json`, #464 — the in-repo source
|
|
60
|
+
* is `data/gazetteer/anchor-lexicon-v1.json`). Gazetteer-trained models (v4.2.0+, whose ONNX
|
|
61
|
+
* declares the `gazetteer_features`/`gazetteer_confidence` inputs) REQUIRE this clue at
|
|
62
|
+
* inference: running them on the zero-filled fallback is the measured train/inference mismatch
|
|
63
|
+
* that wrecks segmentation ("the zero-fill trap", CONTRIBUTING_MODEL_WORK.mdx eval invariants).
|
|
64
|
+
*
|
|
65
|
+
* Defaults to `anchor-lexicon-v1.json` next to `modelUrl`. A fetch miss (404 etc.) does NOT throw
|
|
66
|
+
* — older bundles never shipped the file — but if the loaded model turns out to be
|
|
67
|
+
* gazetteer-trained the loader logs a loud `console.error` naming the missing file and the model
|
|
68
|
+
* runs gazetteer-off (structurally valid, quality-degraded). Pass `null` to skip the fetch
|
|
69
|
+
* entirely.
|
|
70
|
+
*/
|
|
71
|
+
gazetteerLexiconUrl?: string | null;
|
|
72
|
+
/**
|
|
73
|
+
* Channel choreography (#464, v0.9.13 postcode fix): zero the gazetteer clue on pieces adjacent
|
|
74
|
+
* to a postcode-anchor hit. Defaults to TRUE — it pairs with the train-time half on every
|
|
75
|
+
* gazetteer-trained bundle (v4.2.0+) and is inert when either channel is absent.
|
|
76
|
+
*/
|
|
77
|
+
suppressGazetteerNearPostcode?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Address-system conventions mode (#511 Tier A, v4.3.0+). Defaults to `"auto"` (read the model's
|
|
80
|
+
* locale head when exported; inert on bundles without `locale_logits`). Pass a `SystemCode` to
|
|
81
|
+
* pin, or `null` to disable.
|
|
82
|
+
*/
|
|
83
|
+
addressSystemConventions?: NeuralAddressClassifierConfig["addressSystemConventions"] | null;
|
|
84
|
+
/**
|
|
85
|
+
* Span bridge (v4.4.0 declared behavior): merge same-tag spans split at intra-token punctuation
|
|
86
|
+
* ("P.O. Box"). Defaults to TRUE per the v4.4.0 ship config (model-card.json: po_box 60.4
|
|
87
|
+
* without, 89.1 with). Pass false to disable for pre-bridge bundles where gate parity matters.
|
|
88
|
+
*/
|
|
89
|
+
bridgePunctuationGaps?: boolean;
|
|
90
|
+
/** Optional fetch override. Defaults to `globalThis.fetch`. */
|
|
91
|
+
fetchImpl?: typeof fetch;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Default location of the gazetteer-anchor lexicon: `anchor-lexicon-v1.json` as a sibling of the
|
|
95
|
+
* model file. Matches how release bundles lay out their version directory (model.onnx,
|
|
96
|
+
* tokenizer.model, model-card.json, postcode-*.bin, anchor-lexicon-v1.json side by side).
|
|
97
|
+
*/
|
|
98
|
+
export declare function defaultGazetteerLexiconUrl(modelUrl: string): string;
|
|
99
|
+
/**
|
|
100
|
+
* Convenience factory: fetch model + tokenizer, build the runner, return a classifier. The
|
|
101
|
+
* tokenizer is loaded via the existing `loadFromBase64` path so this file shares zero Node-only
|
|
102
|
+
* code with `@mailwoman/neural/classifier`'s `loadFromWeights`.
|
|
103
|
+
*
|
|
104
|
+
* The classifier is constructed with the v4.4.0 ship config by default (gazetteer lexicon +
|
|
105
|
+
* postcode anchor when their assets resolve, `suppressGazetteerNearPostcode: true`,
|
|
106
|
+
* `addressSystemConventions: "auto"`, `bridgePunctuationGaps: true`) — every knob is inert on
|
|
107
|
+
* bundles that predate the corresponding channel, so older versions keep decoding unchanged.
|
|
108
|
+
*/
|
|
109
|
+
export declare function loadNeuralClassifierFromUrls(opts: LoadFromUrlsOpts): Promise<LoadResult>;
|
|
110
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAIN,uBAAuB,EACvB,KAAK,6BAA6B,EAGlC,MAAM,2BAA2B,CAAA;AAElC,OAAO,EAAiB,KAAK,wBAAwB,EAAE,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAE3G,YAAY,EAAE,wBAAwB,EAAE,CAAA;AAExC,MAAM,WAAW,UAAU;IAC1B,UAAU,EAAE,uBAAuB,CAAA;IACnC,WAAW,EAAE,wBAAwB,GAAG,IAAI,CAAA;IAC5C;;;OAGG;IACH,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAA;IAChC;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,OAAO,mBAAmB,EAAE,YAAY,CAAA;CAC/D;AAED,MAAM,WAAW,gBAAgB;IAChC,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAA;IAChB,2FAA2F;IAC3F,YAAY,EAAE,MAAM,CAAA;IACpB;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iFAAiF;IACjF,MAAM,CAAC,EAAE,iBAAiB,CAAA;IAC1B;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC;;;;;;;;;;;;OAYG;IACH,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC;;;;OAIG;IACH,6BAA6B,CAAC,EAAE,OAAO,CAAA;IACvC;;;;OAIG;IACH,wBAAwB,CAAC,EAAE,6BAA6B,CAAC,0BAA0B,CAAC,GAAG,IAAI,CAAA;IAC3F;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,+DAA+D;IAC/D,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CACxB;AA6BD;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAInE;AAED;;;;;;;;;GASG;AACH,wBAAsB,4BAA4B,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CA8C9F"}
|
package/out/loader.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Browser-side loader that pairs the existing `MailwomanTokenizer` (whose `loadFromBase64` path is
|
|
7
|
+
* already browser-safe — it doesn't touch Node fs) with a fresh `WebOnnxRunner`, and returns a
|
|
8
|
+
* ready-to-use `NeuralAddressClassifier`.
|
|
9
|
+
*
|
|
10
|
+
* V1 strategy: fetch both `model.onnx` and `tokenizer.model` over HTTP from caller-provided URLs
|
|
11
|
+
* (typically pointing at the same static-asset bundle that ships the resolver's slim WOF DB). The
|
|
12
|
+
* neural weights package `@mailwoman/neural-weights-en-us` is the canonical source of those two
|
|
13
|
+
* files; for a static deploy, copy them into the public bundle and pass the resulting URLs.
|
|
14
|
+
*/
|
|
15
|
+
import { MailwomanTokenizer, NeuralAddressClassifier, parseGazetteerLexicon, PostcodeBinaryResolver, } from "@mailwoman/neural/browser";
|
|
16
|
+
import { WebOnnxRunner } from "./web-onnx-runner.js";
|
|
17
|
+
/** Merge per-binary anchor lookups: union the country posteriors per postcode, mean the centroids. */
|
|
18
|
+
function mergeAnchorLookups(lookups) {
|
|
19
|
+
if (lookups.length === 1)
|
|
20
|
+
return lookups[0];
|
|
21
|
+
const merged = new Map();
|
|
22
|
+
for (const lookup of lookups) {
|
|
23
|
+
for (const [postcode, entry] of lookup) {
|
|
24
|
+
const existing = merged.get(postcode);
|
|
25
|
+
if (!existing) {
|
|
26
|
+
merged.set(postcode, { posterior: { ...entry.posterior }, lat: entry.lat, lon: entry.lon });
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
for (const country of Object.keys(entry.posterior))
|
|
30
|
+
existing.posterior[country] = 1;
|
|
31
|
+
// Average a real centroid in; ignore (0,0) placeholders.
|
|
32
|
+
if (entry.lat !== 0 || entry.lon !== 0) {
|
|
33
|
+
if (existing.lat === 0 && existing.lon === 0) {
|
|
34
|
+
existing.lat = entry.lat;
|
|
35
|
+
existing.lon = entry.lon;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
existing.lat = (existing.lat + entry.lat) / 2;
|
|
39
|
+
existing.lon = (existing.lon + entry.lon) / 2;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return merged;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Default location of the gazetteer-anchor lexicon: `anchor-lexicon-v1.json` as a sibling of the
|
|
48
|
+
* model file. Matches how release bundles lay out their version directory (model.onnx,
|
|
49
|
+
* tokenizer.model, model-card.json, postcode-*.bin, anchor-lexicon-v1.json side by side).
|
|
50
|
+
*/
|
|
51
|
+
export function defaultGazetteerLexiconUrl(modelUrl) {
|
|
52
|
+
// Swap the final path segment — string surgery rather than `new URL()` so relative model URLs
|
|
53
|
+
// ("/static/mailwoman/model.onnx") stay relative.
|
|
54
|
+
return modelUrl.replace(/[^/]*$/, "anchor-lexicon-v1.json");
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Convenience factory: fetch model + tokenizer, build the runner, return a classifier. The
|
|
58
|
+
* tokenizer is loaded via the existing `loadFromBase64` path so this file shares zero Node-only
|
|
59
|
+
* code with `@mailwoman/neural/classifier`'s `loadFromWeights`.
|
|
60
|
+
*
|
|
61
|
+
* The classifier is constructed with the v4.4.0 ship config by default (gazetteer lexicon +
|
|
62
|
+
* postcode anchor when their assets resolve, `suppressGazetteerNearPostcode: true`,
|
|
63
|
+
* `addressSystemConventions: "auto"`, `bridgePunctuationGaps: true`) — every knob is inert on
|
|
64
|
+
* bundles that predate the corresponding channel, so older versions keep decoding unchanged.
|
|
65
|
+
*/
|
|
66
|
+
export async function loadNeuralClassifierFromUrls(opts) {
|
|
67
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
68
|
+
if (!fetchImpl) {
|
|
69
|
+
throw new Error("no fetch implementation available — pass fetchImpl in non-fetch environments");
|
|
70
|
+
}
|
|
71
|
+
const gazetteerLexiconUrl = opts.gazetteerLexiconUrl === null ? null : (opts.gazetteerLexiconUrl ?? defaultGazetteerLexiconUrl(opts.modelUrl));
|
|
72
|
+
const [modelBytes, tokenizerBytes, labels, gazetteerLexicon] = await Promise.all([
|
|
73
|
+
fetchBytes(opts.modelUrl, fetchImpl),
|
|
74
|
+
fetchBytes(opts.tokenizerUrl, fetchImpl),
|
|
75
|
+
opts.modelCardUrl ? fetchLabelsFromModelCard(opts.modelCardUrl, fetchImpl) : Promise.resolve(null),
|
|
76
|
+
gazetteerLexiconUrl ? fetchGazetteerLexicon(gazetteerLexiconUrl, fetchImpl) : Promise.resolve(null),
|
|
77
|
+
]);
|
|
78
|
+
const [tokenizer, runner, postcodeAnchorLookup] = await Promise.all([
|
|
79
|
+
MailwomanTokenizer.loadFromBase64(toBase64(tokenizerBytes)),
|
|
80
|
+
WebOnnxRunner.fromBytes(modelBytes, opts.runner),
|
|
81
|
+
opts.postcodeBinaryUrls?.length
|
|
82
|
+
? Promise.all(opts.postcodeBinaryUrls.map(async (url) => new PostcodeBinaryResolver(await fetchBytes(url, fetchImpl)).toAnchorLookup())).then(mergeAnchorLookups)
|
|
83
|
+
: Promise.resolve(undefined),
|
|
84
|
+
]);
|
|
85
|
+
const conventions = opts.addressSystemConventions === null ? undefined : (opts.addressSystemConventions ?? "auto");
|
|
86
|
+
const classifier = new NeuralAddressClassifier({
|
|
87
|
+
tokenizer,
|
|
88
|
+
runner,
|
|
89
|
+
...(labels ? { labels } : {}),
|
|
90
|
+
...(postcodeAnchorLookup ? { postcodeAnchorLookup } : {}),
|
|
91
|
+
...(gazetteerLexicon ? { gazetteerLexicon } : {}),
|
|
92
|
+
suppressGazetteerNearPostcode: opts.suppressGazetteerNearPostcode ?? true,
|
|
93
|
+
...(conventions ? { addressSystemConventions: conventions } : {}),
|
|
94
|
+
bridgePunctuationGaps: opts.bridgePunctuationGaps ?? true,
|
|
95
|
+
});
|
|
96
|
+
await runner.infer([0]);
|
|
97
|
+
warnOnUnfedTrainedChannels(runner, {
|
|
98
|
+
gazetteerLexicon,
|
|
99
|
+
gazetteerLexiconUrl,
|
|
100
|
+
postcodeAnchorLookup,
|
|
101
|
+
});
|
|
102
|
+
return { classifier, diagnostics: runner.diagnostics, labels };
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Loud degrade (#464): the warmup `infer([0])` above forced session creation, so the graph's
|
|
106
|
+
* declared inputs are now known. A gazetteer/anchor-TRAINED model running on the zero-filled
|
|
107
|
+
* fallback is a measured failure mode (train/inference mismatch — "the zero-fill trap"), not a
|
|
108
|
+
* quality-neutral default; without this check the only symptom would be silently degraded parses.
|
|
109
|
+
* (Pre-fix, the symptom was worse still: ORT's cryptic `input 'gazetteer_features' is missing in
|
|
110
|
+
* 'feeds'`.) The loader still returns a working classifier — structural fallback, loud console.
|
|
111
|
+
*/
|
|
112
|
+
function warnOnUnfedTrainedChannels(runner, fed) {
|
|
113
|
+
const inputNames = runner.inputNames;
|
|
114
|
+
if (!inputNames)
|
|
115
|
+
return;
|
|
116
|
+
if (inputNames.includes("gazetteer_features") && !fed.gazetteerLexicon) {
|
|
117
|
+
console.error("[mailwoman/neural-web] This model is gazetteer-anchor-trained (its ONNX declares `gazetteer_features`) " +
|
|
118
|
+
"but no gazetteer lexicon was loaded" +
|
|
119
|
+
(fed.gazetteerLexiconUrl
|
|
120
|
+
? ` — \`anchor-lexicon-v1.json\` could not be fetched from ${fed.gazetteerLexiconUrl}. ` +
|
|
121
|
+
"Upload the lexicon next to model.onnx, or pass `gazetteerLexiconUrl` explicitly."
|
|
122
|
+
: " — `gazetteerLexiconUrl` was explicitly disabled (null). ") +
|
|
123
|
+
" Running with zero-filled gazetteer clues: parses will be degraded (train/inference mismatch).");
|
|
124
|
+
}
|
|
125
|
+
if (inputNames.includes("anchor_features") && !fed.postcodeAnchorLookup) {
|
|
126
|
+
console.error("[mailwoman/neural-web] This model is postcode-anchor-trained (its ONNX declares `anchor_features`) " +
|
|
127
|
+
"but no `postcodeBinaryUrls` were provided (postcode-<cc>.bin). " +
|
|
128
|
+
"Running with zero-filled anchor features: the anchor-off identity, degraded vs the ship config.");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Fetch + parse `anchor-lexicon-v1.json`. A missing file (404 or network failure) returns null —
|
|
133
|
+
* the caller decides whether that matters (it does iff the model declares the gazetteer inputs; see
|
|
134
|
+
* `warnOnUnfedTrainedChannels`). A PRESENT-but-malformed lexicon throws loudly via
|
|
135
|
+
* `parseGazetteerLexicon`'s validation — never silently zero-fill off bad data.
|
|
136
|
+
*/
|
|
137
|
+
async function fetchGazetteerLexicon(url, fetchImpl) {
|
|
138
|
+
let res;
|
|
139
|
+
try {
|
|
140
|
+
res = await fetchImpl(url);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
if (!res.ok)
|
|
146
|
+
return null;
|
|
147
|
+
return parseGazetteerLexicon((await res.json()));
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Browser-side analogue of `weights.readLabelsFromModelCard`. Same shape contract: returns the
|
|
151
|
+
* `labels` array only when the card has a non-empty string array, throws on a present-but-malformed
|
|
152
|
+
* field, returns `null` when the field is simply absent (legacy pre-v0.4.0 card).
|
|
153
|
+
*
|
|
154
|
+
* A 404 on the model-card itself is treated as "no card provided" — we tolerate older bundles that
|
|
155
|
+
* shipped without one and let the classifier fall back to its compile-time default.
|
|
156
|
+
*/
|
|
157
|
+
async function fetchLabelsFromModelCard(url, fetchImpl) {
|
|
158
|
+
const res = await fetchImpl(url);
|
|
159
|
+
if (!res.ok) {
|
|
160
|
+
if (res.status === 404)
|
|
161
|
+
return null;
|
|
162
|
+
throw new Error(`fetch ${url} failed: ${res.status} ${res.statusText}`);
|
|
163
|
+
}
|
|
164
|
+
const parsed = (await res.json());
|
|
165
|
+
const labels = parsed.labels;
|
|
166
|
+
if (labels === undefined)
|
|
167
|
+
return null;
|
|
168
|
+
if (!Array.isArray(labels) || labels.length === 0 || !labels.every((l) => typeof l === "string")) {
|
|
169
|
+
throw new Error(`model-card at ${url} has a malformed \`labels\` field — ` +
|
|
170
|
+
`expected a non-empty array of strings, got ${JSON.stringify(labels)}.`);
|
|
171
|
+
}
|
|
172
|
+
return Object.freeze(labels.slice());
|
|
173
|
+
}
|
|
174
|
+
async function fetchBytes(url, fetchImpl) {
|
|
175
|
+
const res = await fetchImpl(url);
|
|
176
|
+
if (!res.ok)
|
|
177
|
+
throw new Error(`fetch ${url} failed: ${res.status} ${res.statusText}`);
|
|
178
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Base64-encode a Uint8Array. Browsers + Node 18+ both have `btoa(String.fromCharCode(...))` but
|
|
182
|
+
* String.fromCharCode chokes on long arrays (call-stack overflow on a few MB of bytes). The chunked
|
|
183
|
+
* loop avoids that — kept here rather than imported because both browser and Node need it and
|
|
184
|
+
* adding a dep for ~5 lines is silly.
|
|
185
|
+
*/
|
|
186
|
+
function toBase64(bytes) {
|
|
187
|
+
const chunkSize = 0x8000;
|
|
188
|
+
let binary = "";
|
|
189
|
+
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
190
|
+
const chunk = bytes.subarray(i, i + chunkSize);
|
|
191
|
+
binary += String.fromCharCode(...chunk);
|
|
192
|
+
}
|
|
193
|
+
if (typeof btoa === "function")
|
|
194
|
+
return btoa(binary);
|
|
195
|
+
// Node: Buffer is the lower-friction path; the lazy import keeps the file from pulling in
|
|
196
|
+
// node:buffer when bundlers are statically analyzing browser entries.
|
|
197
|
+
return Buffer.from(binary, "binary").toString("base64");
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAGN,kBAAkB,EAClB,uBAAuB,EAEvB,qBAAqB,EACrB,sBAAsB,GACtB,MAAM,2BAA2B,CAAA;AAElC,OAAO,EAAE,aAAa,EAAyD,MAAM,sBAAsB,CAAA;AAiF3G,sGAAsG;AACtG,SAAS,kBAAkB,CAAC,OAAgC;IAC3D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC,CAAC,CAAE,CAAA;IAC5C,MAAM,MAAM,GAAiB,IAAI,GAAG,EAAE,CAAA;IACtC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;YACxC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YACrC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACf,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAA;gBAC3F,SAAQ;YACT,CAAC;YACD,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;gBAAE,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YACnF,yDAAyD;YACzD,IAAI,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,KAAK,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC;gBACxC,IAAI,QAAQ,CAAC,GAAG,KAAK,CAAC,IAAI,QAAQ,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC;oBAC9C,QAAQ,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAA;oBACxB,QAAQ,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAA;gBACzB,CAAC;qBAAM,CAAC;oBACP,QAAQ,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;oBAC7C,QAAQ,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;gBAC9C,CAAC;YACF,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,MAAM,CAAA;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,QAAgB;IAC1D,8FAA8F;IAC9F,kDAAkD;IAClD,OAAO,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,wBAAwB,CAAC,CAAA;AAC5D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,IAAsB;IACxE,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,KAAK,CAAA;IACpD,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAA;IAChG,CAAC;IAED,MAAM,mBAAmB,GACxB,IAAI,CAAC,mBAAmB,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,IAAI,0BAA0B,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;IAEnH,MAAM,CAAC,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAChF,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC;QACpC,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC;QACxC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,wBAAwB,CAAC,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;QAClG,mBAAmB,CAAC,CAAC,CAAC,qBAAqB,CAAC,mBAAmB,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;KACnG,CAAC,CAAA;IAEF,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,oBAAoB,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACnE,kBAAkB,CAAC,cAAc,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;QAC3D,aAAa,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC;QAChD,IAAI,CAAC,kBAAkB,EAAE,MAAM;YAC9B,CAAC,CAAC,OAAO,CAAC,GAAG,CACX,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CACzC,IAAI,sBAAsB,CAAC,MAAM,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,cAAc,EAAE,CAC7E,CACD,CAAC,IAAI,CAAC,kBAAkB,CAAC;YAC3B,CAAC,CAAC,OAAO,CAAC,OAAO,CAA2B,SAAS,CAAC;KACvD,CAAC,CAAA;IAEF,MAAM,WAAW,GAAG,IAAI,CAAC,wBAAwB,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,wBAAwB,IAAI,MAAM,CAAC,CAAA;IAClH,MAAM,UAAU,GAAG,IAAI,uBAAuB,CAAC;QAC9C,SAAS;QACT,MAAM;QACN,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,oBAAoB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzD,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,6BAA6B,EAAE,IAAI,CAAC,6BAA6B,IAAI,IAAI;QACzE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,wBAAwB,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,qBAAqB,EAAE,IAAI,CAAC,qBAAqB,IAAI,IAAI;KACzD,CAAC,CAAA;IACF,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACvB,0BAA0B,CAAC,MAAM,EAAE;QAClC,gBAAgB;QAChB,mBAAmB;QACnB,oBAAoB;KACpB,CAAC,CAAA;IACF,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,CAAA;AAC/D,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,0BAA0B,CAClC,MAAqB,EACrB,GAIC;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAA;IACpC,IAAI,CAAC,UAAU;QAAE,OAAM;IACvB,IAAI,UAAU,CAAC,QAAQ,CAAC,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACxE,OAAO,CAAC,KAAK,CACZ,yGAAyG;YACxG,qCAAqC;YACrC,CAAC,GAAG,CAAC,mBAAmB;gBACvB,CAAC,CAAC,2DAA2D,GAAG,CAAC,mBAAmB,IAAI;oBACvF,kFAAkF;gBACnF,CAAC,CAAC,2DAA2D,CAAC;YAC/D,gGAAgG,CACjG,CAAA;IACF,CAAC;IACD,IAAI,UAAU,CAAC,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,oBAAoB,EAAE,CAAC;QACzE,OAAO,CAAC,KAAK,CACZ,qGAAqG;YACpG,iEAAiE;YACjE,iGAAiG,CAClG,CAAA;IACF,CAAC;AACF,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,qBAAqB,CAAC,GAAW,EAAE,SAAuB;IACxE,IAAI,GAAa,CAAA;IACjB,IAAI,CAAC;QACJ,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAA;IAC3B,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAA;IACZ,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,OAAO,IAAI,CAAA;IACxB,OAAO,qBAAqB,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAgD,CAAC,CAAA;AAChG,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,wBAAwB,CAAC,GAAW,EAAE,SAAuB;IAC3E,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAA;IAChC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACb,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAA;QACnC,MAAM,IAAI,KAAK,CAAC,SAAS,GAAG,YAAY,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAA;IACxE,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAyB,CAAA;IACzD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAA;IAC5B,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,IAAI,CAAA;IACrC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;QAClG,MAAM,IAAI,KAAK,CACd,iBAAiB,GAAG,sCAAsC;YACzD,8CAA8C,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CACxE,CAAA;IACF,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAsB,CAAA;AAC1D,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,GAAW,EAAE,SAAuB;IAC7D,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAA;IAChC,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,SAAS,GAAG,YAAY,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAA;IACpF,OAAO,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC,CAAA;AAC/C,CAAC;AAED;;;;;GAKG;AACH,SAAS,QAAQ,CAAC,KAAiB;IAClC,MAAM,SAAS,GAAG,MAAM,CAAA;IACxB,IAAI,MAAM,GAAG,EAAE,CAAA;IACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,SAAS,EAAE,CAAC;QAClD,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,CAAA;QAC9C,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC,CAAA;IACxC,CAAC;IACD,IAAI,OAAO,IAAI,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC,MAAM,CAAC,CAAA;IACnD,0FAA0F;IAC1F,sEAAsE;IACtE,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;AACxD,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Browser ONNX inference wrapper. Implements the same `NeuralRunner` contract `@mailwoman/neural`'s
|
|
7
|
+
* classifier consumes, but backed by `onnxruntime-web` (WASM + optional WebGPU) instead of
|
|
8
|
+
* `onnxruntime-node`.
|
|
9
|
+
*
|
|
10
|
+
* Execution provider strategy:
|
|
11
|
+
*
|
|
12
|
+
* - Try WebGPU first when `useWebGpu !== false`. ~10× faster than WASM on supported devices, but
|
|
13
|
+
* availability depends on browser (Chromium 113+, Safari Tech Preview) AND hardware. The
|
|
14
|
+
* runtime surfaces a clean error when WebGPU is unavailable, so the constructor falls back to
|
|
15
|
+
* WASM automatically.
|
|
16
|
+
* - WASM (SIMD when available) is the universal fallback. ~2× slower than WebGPU on the same model
|
|
17
|
+
* but works everywhere onnxruntime-web does — including in Node, which is how the test
|
|
18
|
+
* harness exercises this file.
|
|
19
|
+
*
|
|
20
|
+
* Tensor shape + I/O contract matches `OnnxRunner` exactly: fixed-length int64 inputs, padded with
|
|
21
|
+
* zeros + attention_mask, output is a `logits` tensor of shape `[batch, seq, num_labels]`. See
|
|
22
|
+
* `@mailwoman/neural/onnx-runner` for the full export contract this file mirrors.
|
|
23
|
+
*/
|
|
24
|
+
import { type InferResult, type NeuralRunner } from "@mailwoman/neural/browser";
|
|
25
|
+
export interface WebOnnxRunnerOpts {
|
|
26
|
+
/**
|
|
27
|
+
* Try the WebGPU execution provider first. Defaults to true. Set false to skip the WebGPU probe —
|
|
28
|
+
* useful in test environments where WebGPU isn't available and the probe failure adds latency.
|
|
29
|
+
*/
|
|
30
|
+
useWebGpu?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Fixed sequence length the model expects. Matches `OnnxRunner.DEFAULT_FIXED_SEQ_LEN` (128) by
|
|
33
|
+
* default. Re-quantized models can override.
|
|
34
|
+
*/
|
|
35
|
+
fixedSeqLen?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Optional override for where onnxruntime-web should load its `.wasm` assets from. Defaults to
|
|
38
|
+
* the package's CDN paths; bundlers usually want to point this at a self-hosted copy.
|
|
39
|
+
*
|
|
40
|
+
* Example: `setWasmPaths("/static/ort/")` and put the .wasm files at /static/ort/.
|
|
41
|
+
*/
|
|
42
|
+
wasmPathsRoot?: string;
|
|
43
|
+
}
|
|
44
|
+
export declare const DEFAULT_FIXED_SEQ_LEN = 128;
|
|
45
|
+
export interface WebOnnxRunnerDiagnostics {
|
|
46
|
+
backend: "webgpu" | "wasm";
|
|
47
|
+
modelBytes: number;
|
|
48
|
+
}
|
|
49
|
+
export declare class WebOnnxRunner implements NeuralRunner {
|
|
50
|
+
#private;
|
|
51
|
+
private readonly modelBytes;
|
|
52
|
+
private readonly opts;
|
|
53
|
+
readonly fixedSeqLen: number;
|
|
54
|
+
diagnostics: WebOnnxRunnerDiagnostics | null;
|
|
55
|
+
private constructor();
|
|
56
|
+
/** Construct from already-fetched model bytes. */
|
|
57
|
+
static fromBytes(modelBytes: Uint8Array, opts?: WebOnnxRunnerOpts): Promise<WebOnnxRunner>;
|
|
58
|
+
/** Fetch the model from a URL and construct. */
|
|
59
|
+
static fromUrl(modelUrl: string, opts?: WebOnnxRunnerOpts): Promise<WebOnnxRunner>;
|
|
60
|
+
/**
|
|
61
|
+
* Names of the inputs the loaded ONNX graph declares. `null` until the session has been created
|
|
62
|
+
* (first `infer()` call). Lets callers (e.g. the neural-web loader) detect
|
|
63
|
+
* anchor/gazetteer-trained models and warn loudly when the corresponding feature source wasn't
|
|
64
|
+
* provided — running such a model on the zero-filled fallback is the measured train/inference
|
|
65
|
+
* mismatch ("the zero-fill trap"), not a quality-neutral degrade.
|
|
66
|
+
*/
|
|
67
|
+
get inputNames(): readonly string[] | null;
|
|
68
|
+
infer(tokenIds: number[], anchor?: {
|
|
69
|
+
features: ReadonlyArray<ReadonlyArray<number>>;
|
|
70
|
+
confidence: ReadonlyArray<number>;
|
|
71
|
+
}, gazetteer?: {
|
|
72
|
+
features: ReadonlyArray<ReadonlyArray<number>>;
|
|
73
|
+
confidence: ReadonlyArray<number>;
|
|
74
|
+
}): Promise<InferResult>;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=web-onnx-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-onnx-runner.d.ts","sourceRoot":"","sources":["../web-onnx-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAGN,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,MAAM,2BAA2B,CAAA;AAGlC,MAAM,WAAW,iBAAiB;IACjC;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,eAAO,MAAM,qBAAqB,MAAM,CAAA;AAUxC,MAAM,WAAW,wBAAwB;IACxC,OAAO,EAAE,QAAQ,GAAG,MAAM,CAAA;IAC1B,UAAU,EAAE,MAAM,CAAA;CAClB;AAED,qBAAa,aAAc,YAAW,YAAY;;IAOhD,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI;IAPtB,SAAgB,WAAW,EAAE,MAAM,CAAA;IAC5B,WAAW,EAAE,wBAAwB,GAAG,IAAI,CAAO;IAI1D,OAAO;IAOP,kDAAkD;WACrC,SAAS,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,aAAa,CAAC;IAMpG,gDAAgD;WACnC,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,aAAa,CAAC;IAqC5F;;;;;;OAMG;IACH,IAAI,UAAU,IAAI,SAAS,MAAM,EAAE,GAAG,IAAI,CAEzC;IAEK,KAAK,CACV,QAAQ,EAAE,MAAM,EAAE,EAClB,MAAM,CAAC,EAAE;QAAE,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;QAAC,UAAU,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;KAAE,EAC9F,SAAS,CAAC,EAAE;QAAE,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;QAAC,UAAU,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;KAAE,GAC/F,OAAO,CAAC,WAAW,CAAC;CAoFvB"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Browser ONNX inference wrapper. Implements the same `NeuralRunner` contract `@mailwoman/neural`'s
|
|
7
|
+
* classifier consumes, but backed by `onnxruntime-web` (WASM + optional WebGPU) instead of
|
|
8
|
+
* `onnxruntime-node`.
|
|
9
|
+
*
|
|
10
|
+
* Execution provider strategy:
|
|
11
|
+
*
|
|
12
|
+
* - Try WebGPU first when `useWebGpu !== false`. ~10× faster than WASM on supported devices, but
|
|
13
|
+
* availability depends on browser (Chromium 113+, Safari Tech Preview) AND hardware. The
|
|
14
|
+
* runtime surfaces a clean error when WebGPU is unavailable, so the constructor falls back to
|
|
15
|
+
* WASM automatically.
|
|
16
|
+
* - WASM (SIMD when available) is the universal fallback. ~2× slower than WebGPU on the same model
|
|
17
|
+
* but works everywhere onnxruntime-web does — including in Node, which is how the test
|
|
18
|
+
* harness exercises this file.
|
|
19
|
+
*
|
|
20
|
+
* Tensor shape + I/O contract matches `OnnxRunner` exactly: fixed-length int64 inputs, padded with
|
|
21
|
+
* zeros + attention_mask, output is a `logits` tensor of shape `[batch, seq, num_labels]`. See
|
|
22
|
+
* `@mailwoman/neural/onnx-runner` for the full export contract this file mirrors.
|
|
23
|
+
*/
|
|
24
|
+
import { ANCHOR_FEATURE_DIM, GAZETTEER_FEATURE_DIM, } from "@mailwoman/neural/browser";
|
|
25
|
+
import * as ort from "onnxruntime-web/webgpu";
|
|
26
|
+
export const DEFAULT_FIXED_SEQ_LEN = 128;
|
|
27
|
+
/** Apply `wasmPathsRoot` once at module init. Safe to call multiple times. */
|
|
28
|
+
function configureWasmPaths(root) {
|
|
29
|
+
if (!root)
|
|
30
|
+
return;
|
|
31
|
+
// onnxruntime-web ships this on `ort.env.wasm`. We assign directly rather than calling
|
|
32
|
+
// `setWasmPaths` so it works across the slightly different shapes the typings have had.
|
|
33
|
+
ort.env.wasm.wasmPaths = root;
|
|
34
|
+
}
|
|
35
|
+
export class WebOnnxRunner {
|
|
36
|
+
modelBytes;
|
|
37
|
+
opts;
|
|
38
|
+
fixedSeqLen;
|
|
39
|
+
diagnostics = null;
|
|
40
|
+
#session = null;
|
|
41
|
+
#loadPromise = null;
|
|
42
|
+
constructor(modelBytes, opts) {
|
|
43
|
+
this.modelBytes = modelBytes;
|
|
44
|
+
this.opts = opts;
|
|
45
|
+
this.fixedSeqLen = opts.fixedSeqLen ?? DEFAULT_FIXED_SEQ_LEN;
|
|
46
|
+
}
|
|
47
|
+
/** Construct from already-fetched model bytes. */
|
|
48
|
+
static async fromBytes(modelBytes, opts = {}) {
|
|
49
|
+
configureWasmPaths(opts.wasmPathsRoot);
|
|
50
|
+
const runner = new WebOnnxRunner(modelBytes, opts);
|
|
51
|
+
return runner;
|
|
52
|
+
}
|
|
53
|
+
/** Fetch the model from a URL and construct. */
|
|
54
|
+
static async fromUrl(modelUrl, opts = {}) {
|
|
55
|
+
const res = await fetch(modelUrl);
|
|
56
|
+
if (!res.ok)
|
|
57
|
+
throw new Error(`fetch ${modelUrl} failed: ${res.status} ${res.statusText}`);
|
|
58
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
59
|
+
return WebOnnxRunner.fromBytes(bytes, opts);
|
|
60
|
+
}
|
|
61
|
+
async #ensureSession() {
|
|
62
|
+
if (this.#session)
|
|
63
|
+
return this.#session;
|
|
64
|
+
if (!this.#loadPromise) {
|
|
65
|
+
this.#loadPromise = (async () => {
|
|
66
|
+
const wantWebGpu = this.opts.useWebGpu !== false;
|
|
67
|
+
if (wantWebGpu) {
|
|
68
|
+
try {
|
|
69
|
+
const session = await ort.InferenceSession.create(this.modelBytes, {
|
|
70
|
+
executionProviders: ["webgpu", "wasm"],
|
|
71
|
+
graphOptimizationLevel: "all",
|
|
72
|
+
});
|
|
73
|
+
this.#session = session;
|
|
74
|
+
this.diagnostics = { backend: "webgpu", modelBytes: this.modelBytes.byteLength };
|
|
75
|
+
return session;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// WebGPU probe failed — fall through to WASM
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const session = await ort.InferenceSession.create(this.modelBytes, {
|
|
82
|
+
executionProviders: ["wasm"],
|
|
83
|
+
graphOptimizationLevel: "all",
|
|
84
|
+
});
|
|
85
|
+
this.#session = session;
|
|
86
|
+
this.diagnostics = { backend: "wasm", modelBytes: this.modelBytes.byteLength };
|
|
87
|
+
return session;
|
|
88
|
+
})();
|
|
89
|
+
}
|
|
90
|
+
return this.#loadPromise;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Names of the inputs the loaded ONNX graph declares. `null` until the session has been created
|
|
94
|
+
* (first `infer()` call). Lets callers (e.g. the neural-web loader) detect
|
|
95
|
+
* anchor/gazetteer-trained models and warn loudly when the corresponding feature source wasn't
|
|
96
|
+
* provided — running such a model on the zero-filled fallback is the measured train/inference
|
|
97
|
+
* mismatch ("the zero-fill trap"), not a quality-neutral degrade.
|
|
98
|
+
*/
|
|
99
|
+
get inputNames() {
|
|
100
|
+
return this.#session?.inputNames ?? null;
|
|
101
|
+
}
|
|
102
|
+
async infer(tokenIds, anchor, gazetteer) {
|
|
103
|
+
const session = await this.#ensureSession();
|
|
104
|
+
const seqLen = Math.min(tokenIds.length, this.fixedSeqLen);
|
|
105
|
+
const padded = new BigInt64Array(this.fixedSeqLen);
|
|
106
|
+
const mask = new BigInt64Array(this.fixedSeqLen);
|
|
107
|
+
for (let i = 0; i < seqLen; i++) {
|
|
108
|
+
padded[i] = BigInt(tokenIds[i]);
|
|
109
|
+
mask[i] = 1n;
|
|
110
|
+
}
|
|
111
|
+
const feeds = {
|
|
112
|
+
input_ids: new ort.Tensor("int64", padded, [1, this.fixedSeqLen]),
|
|
113
|
+
attention_mask: new ort.Tensor("int64", mask, [1, this.fixedSeqLen]),
|
|
114
|
+
};
|
|
115
|
+
// Anchor channel (#239/#240) — mirror of the node OnnxRunner. Feed the per-piece anchor when the
|
|
116
|
+
// caller supplies it; otherwise, for anchor-trained models (whose ONNX declares the inputs as
|
|
117
|
+
// mandatory), feed zeros — the confidence=0 identity / anchor-off path. Without this the session
|
|
118
|
+
// throws on the missing required inputs.
|
|
119
|
+
if (anchor) {
|
|
120
|
+
const dim = anchor.features[0]?.length ?? 0;
|
|
121
|
+
const af = new Float32Array(this.fixedSeqLen * dim);
|
|
122
|
+
const ac = new Float32Array(this.fixedSeqLen);
|
|
123
|
+
for (let i = 0; i < seqLen; i++) {
|
|
124
|
+
ac[i] = anchor.confidence[i] ?? 0;
|
|
125
|
+
const row = anchor.features[i];
|
|
126
|
+
if (row)
|
|
127
|
+
for (let d = 0; d < dim; d++)
|
|
128
|
+
af[i * dim + d] = row[d] ?? 0;
|
|
129
|
+
}
|
|
130
|
+
feeds.anchor_features = new ort.Tensor("float32", af, [1, this.fixedSeqLen, dim]);
|
|
131
|
+
feeds.anchor_confidence = new ort.Tensor("float32", ac, [1, this.fixedSeqLen]);
|
|
132
|
+
}
|
|
133
|
+
else if (session.inputNames.includes("anchor_features")) {
|
|
134
|
+
feeds.anchor_features = new ort.Tensor("float32", new Float32Array(this.fixedSeqLen * ANCHOR_FEATURE_DIM), [
|
|
135
|
+
1,
|
|
136
|
+
this.fixedSeqLen,
|
|
137
|
+
ANCHOR_FEATURE_DIM,
|
|
138
|
+
]);
|
|
139
|
+
feeds.anchor_confidence = new ort.Tensor("float32", new Float32Array(this.fixedSeqLen), [1, this.fixedSeqLen]);
|
|
140
|
+
}
|
|
141
|
+
// Gazetteer-anchor channel (#464) — mirror of the node OnnxRunner. Feed the per-piece clue when
|
|
142
|
+
// the caller supplies it AND the graph declares the inputs; for gazetteer-trained models with no
|
|
143
|
+
// clue data, feed zeros (the confidence=0 identity — a structural fallback only; see the loader's
|
|
144
|
+
// loud warning) so the session doesn't throw `input 'gazetteer_features' is missing in 'feeds'`.
|
|
145
|
+
if (gazetteer && session.inputNames.includes("gazetteer_features")) {
|
|
146
|
+
const dim = gazetteer.features[0]?.length ?? 0;
|
|
147
|
+
const gf = new Float32Array(this.fixedSeqLen * dim);
|
|
148
|
+
const gc = new Float32Array(this.fixedSeqLen);
|
|
149
|
+
for (let i = 0; i < seqLen; i++) {
|
|
150
|
+
gc[i] = gazetteer.confidence[i] ?? 0;
|
|
151
|
+
const row = gazetteer.features[i];
|
|
152
|
+
if (row)
|
|
153
|
+
for (let d = 0; d < dim; d++)
|
|
154
|
+
gf[i * dim + d] = row[d] ?? 0;
|
|
155
|
+
}
|
|
156
|
+
feeds.gazetteer_features = new ort.Tensor("float32", gf, [1, this.fixedSeqLen, dim]);
|
|
157
|
+
feeds.gazetteer_confidence = new ort.Tensor("float32", gc, [1, this.fixedSeqLen]);
|
|
158
|
+
}
|
|
159
|
+
else if (session.inputNames.includes("gazetteer_features")) {
|
|
160
|
+
feeds.gazetteer_features = new ort.Tensor("float32", new Float32Array(this.fixedSeqLen * GAZETTEER_FEATURE_DIM), [
|
|
161
|
+
1,
|
|
162
|
+
this.fixedSeqLen,
|
|
163
|
+
GAZETTEER_FEATURE_DIM,
|
|
164
|
+
]);
|
|
165
|
+
feeds.gazetteer_confidence = new ort.Tensor("float32", new Float32Array(this.fixedSeqLen), [1, this.fixedSeqLen]);
|
|
166
|
+
}
|
|
167
|
+
const output = await session.run(feeds);
|
|
168
|
+
const logitsTensor = output["logits"];
|
|
169
|
+
if (!logitsTensor)
|
|
170
|
+
throw new Error("ONNX model did not return a `logits` output");
|
|
171
|
+
const data = logitsTensor.data;
|
|
172
|
+
const [, , numLabels] = logitsTensor.dims;
|
|
173
|
+
const logits = [];
|
|
174
|
+
for (let t = 0; t < seqLen; t++) {
|
|
175
|
+
const row = new Array(numLabels);
|
|
176
|
+
const base = t * numLabels;
|
|
177
|
+
for (let l = 0; l < numLabels; l++)
|
|
178
|
+
row[l] = data[base + l];
|
|
179
|
+
logits.push(row);
|
|
180
|
+
}
|
|
181
|
+
// Locale head (#511 Tier A): present on v1.1.0+ exports (shipped v4.3.0+), absent before.
|
|
182
|
+
// Mirrors the node OnnxRunner — `addressSystemConventions: "auto"` depends on this surfacing.
|
|
183
|
+
const localeTensor = output["locale_logits"];
|
|
184
|
+
const localeLogits = localeTensor ? Array.from(localeTensor.data) : undefined;
|
|
185
|
+
return { logits, numLabels, ...(localeLogits ? { localeLogits } : {}) };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=web-onnx-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-onnx-runner.js","sourceRoot":"","sources":["../web-onnx-runner.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mailwoman/neural-web",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Browser-side mailwoman neural runtime. Pairs the existing tokenizer + decoder with an onnxruntime-web inference path (WebGPU primary, WASM SIMD fallback). Drop-in for @mailwoman/neural when targeting a static-asset deploy (Phase B of the demo plan).",
|
|
5
|
+
"license": "AGPL-3.0-only",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/sister-software/mailwoman.git",
|
|
9
|
+
"directory": "neural-web"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"exports": {
|
|
13
|
+
"./package.json": "./package.json",
|
|
14
|
+
".": "./out/index.js"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@mailwoman/core": "4.14.0",
|
|
18
|
+
"@mailwoman/neural": "4.14.0",
|
|
19
|
+
"onnxruntime-web": "1.25.0-dev.20260327-722743c0e2"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"out/**/*.js",
|
|
23
|
+
"out/**/*.js.map",
|
|
24
|
+
"out/**/*.d.ts",
|
|
25
|
+
"out/**/*.d.ts.map",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
}
|
|
31
|
+
}
|