mailwoman 7.8.1 → 8.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.
- package/commands/eval/oa-resolver.tsx +1 -1
- package/commands/parse.tsx +72 -19
- package/commands/release/hf.tsx +6 -0
- package/eval-harness/baselines.json +577 -0
- package/eval-harness/gates/v0.5.0-bridge.json +33 -0
- package/eval-harness/gates/v1.6.0-boundary-stress.json +29 -0
- package/eval-harness/gates/v1.7.0-boundary-stress.json +31 -0
- package/eval-harness/gates/v2.0.0-width.json +21 -0
- package/eval-harness/gates/v2.3.0-nl-postcode.json +34 -0
- package/eval-harness/gates/v4.15.0-boundary.json +29 -0
- package/eval-harness/gates/v4.2.0-ship.json +21 -0
- package/eval-harness/gates/v4.3.0-relabel.json +23 -0
- package/eval-harness/gates/v4.4.0-boundary.json +29 -0
- package/eval-harness/gates/v5.2.0-nordic.json +30 -0
- package/eval-harness/gates/v5.3.0-family.json +33 -0
- package/eval-harness/gates/v6.0.0-shipped-baseline.json +37 -0
- package/eval-harness/gauntlet/build-regression-db.ts +1 -0
- package/eval-harness/gauntlet/cases/regression.ts +301 -2
- package/eval-harness/gauntlet/harness.ts +28 -5
- package/eval-harness/gauntlet/regression.ts +19 -2
- package/eval-harness/gauntlet/schema.ts +5 -1
- package/eval-harness/oa-resolver-eval.ts +1 -3
- package/geocode-core.ts +18 -1
- package/out/commands/eval/oa-resolver.d.ts +0 -1
- package/out/commands/eval/oa-resolver.d.ts.map +1 -1
- package/out/commands/eval/oa-resolver.js +0 -1
- package/out/commands/eval/oa-resolver.js.map +1 -1
- package/out/commands/parse.d.ts.map +1 -1
- package/out/commands/parse.js +64 -17
- package/out/commands/parse.js.map +1 -1
- package/out/commands/release/hf.d.ts +1 -0
- package/out/commands/release/hf.d.ts.map +1 -1
- package/out/commands/release/hf.js +4 -0
- package/out/commands/release/hf.js.map +1 -1
- package/out/eval-harness/gauntlet/build-regression-db.d.ts.map +1 -1
- package/out/eval-harness/gauntlet/build-regression-db.js +1 -1
- package/out/eval-harness/gauntlet/build-regression-db.js.map +1 -1
- package/out/eval-harness/gauntlet/cases/regression.d.ts +2 -0
- package/out/eval-harness/gauntlet/cases/regression.d.ts.map +1 -1
- package/out/eval-harness/gauntlet/cases/regression.js +296 -2
- package/out/eval-harness/gauntlet/cases/regression.js.map +1 -1
- package/out/eval-harness/gauntlet/harness.d.ts +10 -2
- package/out/eval-harness/gauntlet/harness.d.ts.map +1 -1
- package/out/eval-harness/gauntlet/harness.js +16 -4
- package/out/eval-harness/gauntlet/harness.js.map +1 -1
- package/out/eval-harness/gauntlet/regression.d.ts.map +1 -1
- package/out/eval-harness/gauntlet/regression.js +18 -2
- package/out/eval-harness/gauntlet/regression.js.map +1 -1
- package/out/eval-harness/gauntlet/schema.d.ts +4 -2
- package/out/eval-harness/gauntlet/schema.d.ts.map +1 -1
- package/out/eval-harness/gauntlet/schema.js +2 -0
- package/out/eval-harness/gauntlet/schema.js.map +1 -1
- package/out/eval-harness/oa-resolver-eval.d.ts +0 -2
- package/out/eval-harness/oa-resolver-eval.d.ts.map +1 -1
- package/out/eval-harness/oa-resolver-eval.js +1 -1
- package/out/eval-harness/oa-resolver-eval.js.map +1 -1
- package/out/geocode-core.d.ts.map +1 -1
- package/out/geocode-core.js +15 -1
- package/out/geocode-core.js.map +1 -1
- package/out/release-tools/publish-hf.d.ts +3 -2
- package/out/release-tools/publish-hf.d.ts.map +1 -1
- package/out/release-tools/publish-hf.js +65 -15
- package/out/release-tools/publish-hf.js.map +1 -1
- package/out/runtime-pipeline.d.ts +13 -12
- package/out/runtime-pipeline.d.ts.map +1 -1
- package/out/runtime-pipeline.js +73 -2
- package/out/runtime-pipeline.js.map +1 -1
- package/package.json +26 -24
- package/release-tools/publish-hf.ts +77 -18
- package/runtime-pipeline.ts +99 -4
|
@@ -44,7 +44,7 @@ const OptionsSchema = zod.object({
|
|
|
44
44
|
// scripts/eval shim the probes spawn) keep the original two-key contract.
|
|
45
45
|
adminCoherenceOff: zod.boolean().default(false).describe("#895 tri-state pin: force adminCoherence OFF"),
|
|
46
46
|
hierarchyCompletion: zod.boolean().default(false).describe("#405: recover the dual-role-place locality"),
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
// coordinate tiers
|
|
49
49
|
postcodeAnchor: zod.boolean().default(false).describe("Add the neural+anchor row (anchor-centroid coordinate)"),
|
|
50
50
|
postcodeShards: zod.string().optional().describe("Postcode shards for the anchor rows (comma-separated)"),
|
package/commands/parse.tsx
CHANGED
|
@@ -11,10 +11,10 @@ import { collectProposals, filterByPolicy, InMemoryPolicyRegistry, type PolicyMo
|
|
|
11
11
|
import type { ComponentTag, Section } from "@mailwoman/core/types"
|
|
12
12
|
import { createNeuralProposalClassifier, NeuralAddressClassifier } from "@mailwoman/neural"
|
|
13
13
|
import { weightsPackageName } from "@mailwoman/neural/weights"
|
|
14
|
-
import { createWOFResolver, type Resolver
|
|
14
|
+
import { createWOFResolver, type Resolver } from "@mailwoman/resolver"
|
|
15
15
|
import { Text } from "ink"
|
|
16
16
|
import { createRuntimePipeline } from "mailwoman"
|
|
17
|
-
import React from "react"
|
|
17
|
+
import type React from "react"
|
|
18
18
|
import zod from "zod"
|
|
19
19
|
|
|
20
20
|
import { type CommandComponent, commandError, useCommandTask } from "../cli-kit/index.ts"
|
|
@@ -396,17 +396,27 @@ function serializeTree(
|
|
|
396
396
|
}
|
|
397
397
|
|
|
398
398
|
/**
|
|
399
|
-
*
|
|
400
|
-
*
|
|
401
|
-
*
|
|
402
|
-
*
|
|
399
|
+
* The generic degraded-mode banner (stderr — stdout stays machine-parseable). Emitted when the guard hands back a
|
|
400
|
+
* `declined` outcome (interactive "n" / `--degraded` / a failed download) or the explicit `--no-neural` skip — paths
|
|
401
|
+
* that never attempt an encoder load, so `tryLoadNeural`'s precise absent-vs-load-error warning didn't fire. The
|
|
402
|
+
* attempted-and-failed case is announced by `tryLoadNeural` instead (see #1108), so this banner is deliberately NOT
|
|
403
|
+
* emitted there (it would double up).
|
|
403
404
|
*/
|
|
404
|
-
|
|
405
|
+
function emitDegradedBanner(options: zod.infer<typeof ParseConfigSchema>): void {
|
|
405
406
|
console.error(
|
|
406
407
|
"⚠ degraded parse: the neural encoder is not loaded — output carries structural-pipeline results only.\n" +
|
|
407
408
|
` Upgrade: npm install ${weightsPackageName(options.locale)} or mailwoman parse --download-weights <address>`
|
|
408
409
|
)
|
|
410
|
+
}
|
|
409
411
|
|
|
412
|
+
/**
|
|
413
|
+
* Encoder-less structural parse (plan 3), WITHOUT the banner: the REAL pipeline stages (normalize → query-shape →
|
|
414
|
+
* locale-gate → kind → grouper fast-paths) with no neural classifier. The tree carries what the structural stages can
|
|
415
|
+
* prove (postcode_only / locality_only fast-paths populate it; free-form addresses may yield an empty tree). The caller
|
|
416
|
+
* owns the degraded notice — either {@link emitDegradedBanner} or the precise absent/load-error warning `tryLoadNeural`
|
|
417
|
+
* already printed — so no path degrades silently, and none double-warns.
|
|
418
|
+
*/
|
|
419
|
+
async function runStructuralPipeline(input: string, options: zod.infer<typeof ParseConfigSchema>): Promise<string> {
|
|
410
420
|
const pipeline = createRuntimePipeline({ poiQueryKind: options.poi })
|
|
411
421
|
const result = await pipeline(input, { locale: options.locale })
|
|
412
422
|
|
|
@@ -415,20 +425,37 @@ async function runDegraded(input: string, options: zod.infer<typeof ParseConfigS
|
|
|
415
425
|
: serializeTree(result.tree, options.format, { includeAlternatives: false })
|
|
416
426
|
}
|
|
417
427
|
|
|
428
|
+
/**
|
|
429
|
+
* Structural parse fronted by the generic degraded banner — the guard's `declined` / `--no-neural` entry point (the
|
|
430
|
+
* caller here did NOT attempt an encoder load, so it owns the notice).
|
|
431
|
+
*/
|
|
432
|
+
async function runDegraded(input: string, options: zod.infer<typeof ParseConfigSchema>): Promise<string> {
|
|
433
|
+
emitDegradedBanner(options)
|
|
434
|
+
|
|
435
|
+
return runStructuralPipeline(input, options)
|
|
436
|
+
}
|
|
437
|
+
|
|
418
438
|
/**
|
|
419
439
|
* Default path: runtime pipeline. Lazy-loads the neural classifier + optional resolver. Returns the parsed tree
|
|
420
440
|
* serialized in the requested format. When the encoder is unavailable, degrades to the structural-pipeline stages
|
|
421
441
|
* (normalize → query-shape → kind → grouper fast-paths) rather than any rules parser.
|
|
422
442
|
*/
|
|
423
443
|
async function runPipeline(input: string, options: zod.infer<typeof ParseConfigSchema>): Promise<string> {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
//
|
|
427
|
-
//
|
|
428
|
-
|
|
429
|
-
|
|
444
|
+
// `tryLoadNeural` emits its own precise (absent vs. corrupt/load-error) stderr warning when the load
|
|
445
|
+
// fails, so an attempted-but-failed encoder load is NEVER silent — including on the --resolve/--debug
|
|
446
|
+
// paths, which don't route through the degraded banner below (#1108). The explicit --no-neural skip
|
|
447
|
+
// attempts no load (and gets no such warning); the degraded banner covers it instead.
|
|
448
|
+
const attemptedNeural = !options.noNeural
|
|
449
|
+
const classifier = attemptedNeural ? await tryLoadNeural(options) : undefined
|
|
450
|
+
|
|
451
|
+
// When the encoder isn't loaded and there's no resolver/debug work to do, the full pipeline can only
|
|
452
|
+
// emit QueryShape fast-path structure. Route to the structural path so the CLI still produces useful
|
|
453
|
+
// output for the fast-path kinds (postcode_only, locality_only). `--debug` stays on the pipeline so
|
|
454
|
+
// the operator gets the requested PipelineResult JSON shape.
|
|
430
455
|
if (!classifier && !options.resolve && !options.debug) {
|
|
431
|
-
|
|
456
|
+
// tryLoadNeural already warned when the load was attempted; only the explicit --no-neural skip
|
|
457
|
+
// still needs the generic degraded banner (it never called tryLoadNeural).
|
|
458
|
+
return attemptedNeural ? runStructuralPipeline(input, options) : runDegraded(input, options)
|
|
432
459
|
}
|
|
433
460
|
|
|
434
461
|
const wantAlternatives = options.candidates !== undefined
|
|
@@ -624,7 +651,18 @@ async function runBenchmark(
|
|
|
624
651
|
return lines.join("\n")
|
|
625
652
|
}
|
|
626
653
|
|
|
627
|
-
/**
|
|
654
|
+
/**
|
|
655
|
+
* Try to load the neural classifier. NEVER throws — on failure it emits a LOUD one-line stderr warning and returns
|
|
656
|
+
* `undefined` so the caller degrades to the structural pipeline (postcode_only / locality_only fast-paths still
|
|
657
|
+
* resolve; `npx mailwoman parse …` always produces output). Distinguishes two failure modes (#1108) so a consumer can't
|
|
658
|
+
* attribute silently-degraded output to the neural parser:
|
|
659
|
+
*
|
|
660
|
+
* - Weights ABSENT (package not installed / carries no binaries) → an install hint, no scary error text.
|
|
661
|
+
* - Weights present but the encoder FAILED to load (corrupt / partial bundle) → the underlying error is surfaced, not
|
|
662
|
+
* swallowed.
|
|
663
|
+
*
|
|
664
|
+
* The warning goes to STDERR, never STDOUT, so piped stdout parsing is unaffected.
|
|
665
|
+
*/
|
|
628
666
|
async function tryLoadNeural(
|
|
629
667
|
options: zod.infer<typeof ParseConfigSchema>
|
|
630
668
|
): Promise<NeuralAddressClassifier | undefined> {
|
|
@@ -634,10 +672,25 @@ async function tryLoadNeural(
|
|
|
634
672
|
modelPath: options.model,
|
|
635
673
|
tokenizerPath: options.tokenizer,
|
|
636
674
|
})
|
|
637
|
-
} catch {
|
|
638
|
-
// Graceful degradation: pipeline runs normalize + queryShape + kind + resolver only.
|
|
639
|
-
//
|
|
640
|
-
|
|
675
|
+
} catch (error) {
|
|
676
|
+
// Graceful degradation: pipeline runs normalize + queryShape + kind (+ resolver) only. The caller
|
|
677
|
+
// sees `tree.roots` populated from QueryShape fast-paths but nothing from the encoder — so we warn.
|
|
678
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
679
|
+
// "Absent" = the weights package simply isn't installed (the resolver's not-found signal). Every OTHER
|
|
680
|
+
// failure means the weights DID resolve but the encoder couldn't load them — a partial/metadata-only
|
|
681
|
+
// bundle ("missing model files"), a bad explicit --model/--tokenizer path, or a corrupt artifact — so we
|
|
682
|
+
// surface the underlying error verbatim rather than mislabel it "not installed" and swallow the cause.
|
|
683
|
+
const absent = /Could not resolve/iu.test(message)
|
|
684
|
+
|
|
685
|
+
if (absent) {
|
|
686
|
+
console.error(
|
|
687
|
+
`⚠ neural weights not found — running a degraded structural parse; ` +
|
|
688
|
+
`install ${weightsPackageName(options.locale)} for full accuracy.`
|
|
689
|
+
)
|
|
690
|
+
} else {
|
|
691
|
+
console.error(`⚠ neural weights failed to load — running a degraded structural parse. Encoder error: ${message}`)
|
|
692
|
+
}
|
|
693
|
+
|
|
641
694
|
return undefined
|
|
642
695
|
}
|
|
643
696
|
}
|
package/commands/release/hf.tsx
CHANGED
|
@@ -42,6 +42,12 @@ const OptionsSchema = zod.object({
|
|
|
42
42
|
.describe(
|
|
43
43
|
"Comma-separated placetype-pair-index soft-feed binaries (pair-index-gb.bin,…; placetype-pair-prior arc, Task 8)"
|
|
44
44
|
),
|
|
45
|
+
fsts: zod
|
|
46
|
+
.string()
|
|
47
|
+
.optional()
|
|
48
|
+
.describe(
|
|
49
|
+
"Comma-separated per-locale FST gazetteers for the npm packages (fst-en-us.bin,fst-fr-fr.bin,fst-en-gb.bin; #1318). Staged flat by lowercase basename; publish.yml fetches these into each weights workspace."
|
|
50
|
+
),
|
|
45
51
|
gazetteerLexicon: zod
|
|
46
52
|
.string()
|
|
47
53
|
.optional()
|