@mailwoman/neural-weights-fr-fr 7.2.0 → 7.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mailwoman/neural-weights-fr-fr",
3
- "version": "7.2.0",
3
+ "version": "7.3.0",
4
4
  "description": "Mailwoman neural-classifier weights for locale 'fr-fr'. Data-only overlay — shares the base model.onnx + tokenizer.model from @mailwoman/neural-weights-en-us (they are byte-identical); ships only the fr-specific data siblings. Loaded by @mailwoman/neural at runtime.",
5
5
  "license": "AGPL-3.0-only OR LicenseRef-Commercial",
6
6
  "repository": {
@@ -13,13 +13,21 @@
13
13
  "postcode-fr.bin",
14
14
  "anchor-lexicon-v1.json",
15
15
  "country-surface-lexicon-v1.json",
16
- "README.md"
16
+ "README.md",
17
+ "*.ts",
18
+ "*.tsx",
19
+ "**/*.ts",
20
+ "**/*.tsx",
21
+ "!*.test.ts",
22
+ "!*.test.tsx",
23
+ "!**/*.test.ts",
24
+ "!**/*.test.tsx"
17
25
  ],
18
26
  "publishConfig": {
19
27
  "access": "public"
20
28
  },
21
29
  "dependencies": {
22
- "@mailwoman/neural-weights-en-us": "7.2.0"
30
+ "@mailwoman/neural-weights-en-us": "7.3.0"
23
31
  },
24
32
  "mailwoman": {
25
33
  "baseWeights": "@mailwoman/neural-weights-en-us"
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @copyright Sister Software
4
+ * @license AGPL-3.0
5
+ * @author Teffen Ellis, et al.
6
+ *
7
+ * Symlink dev model + tokenizer files into this package for local testing.
8
+ * See @mailwoman/neural-weights-en-us/scripts/link-dev-weights.ts for the rationale.
9
+ *
10
+ * A single multilingual model serves both en-us and fr-fr (byte-identical artifact;
11
+ * fr-fr just carries its own calibration). Re-symlinks the SAME files as en-us until
12
+ * per-locale training lands. Keep these defaults in lockstep with en-us's DEFAULT_*
13
+ * on every ship (currently v5.1.0 = bsplice-meaninit + the spliced v0.6.0-bsplice
14
+ * tokenizer). The md5 guard reads en-us's model-card `files_md5` — one truth for the
15
+ * one artifact (fr-fr's own card carries no files_md5 block).
16
+ */
17
+
18
+ import { createHash } from "node:crypto"
19
+ import { existsSync, readFileSync, symlinkSync, unlinkSync } from "node:fs"
20
+ import { resolve } from "node:path"
21
+
22
+ import { $public } from "@mailwoman/core/env"
23
+ import { dataRootPath, repoRootPath } from "@mailwoman/core/utils"
24
+
25
+ const PKG_DIR = repoRootPath("neural-weights-fr-fr")
26
+ // In lockstep with en-us's DEFAULT_* (one multilingual artifact serves both) — v5.9.0
27
+ // v241-fr-nsplice-ft pair. The 2026-07-02 ship missed this bump (both linkers still pinned
28
+ // the demo-only v4.16.0 pair); the guard below now fails loud on any future miss.
29
+ const SRC_MODEL =
30
+ $public.MAILWOMAN_DEV_MODEL || dataRootPath("models", "quantized", "model-v241-fr-nsplice-ft-step-12000-int8.onnx")
31
+ const SRC_TOKENIZER =
32
+ $public.MAILWOMAN_DEV_TOKENIZER || dataRootPath("models", "tokenizer", "v0.8.0-fr-nsplice", "tokenizer.model")
33
+
34
+ if (!existsSync(SRC_MODEL)) {
35
+ console.error(`missing source model: ${SRC_MODEL}`)
36
+ process.exit(1)
37
+ }
38
+
39
+ if (!existsSync(SRC_TOKENIZER)) {
40
+ console.error(`missing source tokenizer: ${SRC_TOKENIZER}`)
41
+ process.exit(1)
42
+ }
43
+
44
+ /** Replicate `ln -sf SRC DEST`: drop any pre-existing link/file at the destination, then symlink. */
45
+ function linkForce(src: string, dest: string): void {
46
+ if (existsSync(dest)) {
47
+ unlinkSync(dest)
48
+ }
49
+
50
+ symlinkSync(src, dest)
51
+ }
52
+
53
+ linkForce(SRC_MODEL, resolve(PKG_DIR, "model.onnx"))
54
+ linkForce(SRC_TOKENIZER, resolve(PKG_DIR, "tokenizer.model"))
55
+
56
+ console.log(`linked ${PKG_DIR}/{model.onnx,tokenizer.model}`)
57
+
58
+ // #397 guard, lockstep form: the fr-fr artifact IS the en-us artifact, so verify the
59
+ // linked default bytes against en-us's model-card `files_md5` (skipped under an
60
+ // explicit MAILWOMAN_DEV_* override — deliberate experimentation).
61
+ if (!$public.MAILWOMAN_DEV_MODEL || !$public.MAILWOMAN_DEV_TOKENIZER) {
62
+ const enUSCard = JSON.parse(
63
+ readFileSync(resolve(PKG_DIR, "..", "neural-weights-en-us", "model-card.json"), "utf8")
64
+ ) as { files_md5?: Record<string, string> }
65
+ const checks: Array<[string, string, string | undefined]> = [
66
+ ["model", resolve(PKG_DIR, "model.onnx"), enUSCard.files_md5?.["model.onnx"]],
67
+ ["tokenizer", resolve(PKG_DIR, "tokenizer.model"), enUSCard.files_md5?.["tokenizer.model"]],
68
+ ]
69
+
70
+ for (const [label, path, expected] of checks) {
71
+ if (
72
+ ($public.MAILWOMAN_DEV_MODEL && label === "model") ||
73
+ ($public.MAILWOMAN_DEV_TOKENIZER && label === "tokenizer")
74
+ )
75
+ continue
76
+
77
+ if (!expected) {
78
+ console.error(
79
+ `ERROR (#397 guard): en-us model-card.json has no files_md5 entry for ${label} — cannot verify the dev pin.`
80
+ )
81
+ process.exit(1)
82
+ }
83
+ const actual = createHash("md5").update(readFileSync(path)).digest("hex")
84
+
85
+ if (actual !== expected) {
86
+ console.error(
87
+ `ERROR (#397 guard): linked default ${label} md5 ${actual} != shipped ${expected} (en-us card files_md5).`
88
+ )
89
+ console.error(" Bump this script's SRC_* defaults in lockstep with en-us on each ship.")
90
+ process.exit(1)
91
+ }
92
+ }
93
+ }