@mailwoman/kind-classifier 9.2.0 → 9.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/README.md +2 -2
- package/{classify.ts → lib/classify.ts} +5 -5
- package/{index.ts → lib/index.ts} +10 -10
- package/{intent-markers.ts → lib/intent-markers.ts} +9 -7
- package/{intent-rules.ts → lib/intent-rules.ts} +7 -14
- package/{poi.ts → lib/poi.ts} +63 -5
- package/{rules.ts → lib/rules.ts} +24 -31
- package/lib/types.ts +17 -0
- package/out/classify.d.ts +2 -2
- package/out/classify.d.ts.map +1 -1
- package/out/classify.js +4 -4
- package/out/classify.js.map +1 -1
- package/out/index.d.ts +10 -10
- package/out/index.d.ts.map +1 -1
- package/out/index.js +6 -6
- package/out/index.js.map +1 -1
- package/out/intent-markers.d.ts +3 -3
- package/out/intent-markers.d.ts.map +1 -1
- package/out/intent-markers.js +8 -6
- package/out/intent-markers.js.map +1 -1
- package/out/intent-rules.d.ts +1 -1
- package/out/intent-rules.d.ts.map +1 -1
- package/out/intent-rules.js +6 -12
- package/out/intent-rules.js.map +1 -1
- package/out/poi.d.ts +42 -2
- package/out/poi.d.ts.map +1 -1
- package/out/poi.js +23 -4
- package/out/poi.js.map +1 -1
- package/out/rules.d.ts +12 -4
- package/out/rules.d.ts.map +1 -1
- package/out/rules.js +20 -30
- package/out/rules.js.map +1 -1
- package/out/types.d.ts +3 -25
- package/out/types.d.ts.map +1 -1
- package/out/types.js.map +1 -1
- package/package.json +31 -5
- package/types.ts +0 -34
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ scoreVague(shape: QueryShapeLike): number
|
|
|
46
46
|
## Pipeline position
|
|
47
47
|
|
|
48
48
|
```
|
|
49
|
-
locale-
|
|
49
|
+
locale-hint → kind-classifier → phrase-grouper → classifier → ...
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
## Design
|
|
@@ -59,7 +59,7 @@ locale-gate → kind-classifier → phrase-grouper → classifier → ...
|
|
|
59
59
|
## Related
|
|
60
60
|
|
|
61
61
|
- [`@mailwoman/query-shape`](../query-shape) — feeds structural data into this stage
|
|
62
|
-
- [`@mailwoman/locale-
|
|
62
|
+
- [`@mailwoman/locale-hint`](../locale-hint) — feeds locale context
|
|
63
63
|
- [`@mailwoman/phrase-grouper`](../phrase-grouper) — Stage 2.7, next in the pipeline
|
|
64
64
|
- [Staged Pipeline Contract](https://github.com/sister-software/mailwoman/blob/main/docs/engineering/reference/STAGES.mdx)
|
|
65
65
|
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
* inform the markers without moving the routing decision.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { deriveIntentMarkers } from "
|
|
20
|
-
import { scoreBareToponym, scoreNearMe, scoreRoutePair } from "
|
|
21
|
-
import { createScorePOICategory, createScorePOIQuery, type POIPhraseLookup } from "
|
|
19
|
+
import { deriveIntentMarkers } from "#intent-markers"
|
|
20
|
+
import { scoreBareToponym, scoreNearMe, scoreRoutePair } from "#intent-rules"
|
|
21
|
+
import { createScorePOICategory, createScorePOIQuery, type POIPhraseLookup } from "#poi"
|
|
22
22
|
import {
|
|
23
23
|
scoreIntersection,
|
|
24
24
|
scoreLandmark,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
scoreStructuredAddress,
|
|
29
29
|
scoreVague,
|
|
30
30
|
scoreVenueLandmark,
|
|
31
|
-
} from "
|
|
31
|
+
} from "#rules"
|
|
32
32
|
import type {
|
|
33
33
|
LocaleHint,
|
|
34
34
|
NormalizedInputLite,
|
|
@@ -36,7 +36,7 @@ import type {
|
|
|
36
36
|
QueryKind,
|
|
37
37
|
QueryKindResult,
|
|
38
38
|
QueryShapeLike,
|
|
39
|
-
} from "
|
|
39
|
+
} from "#types"
|
|
40
40
|
|
|
41
41
|
interface KindScorer {
|
|
42
42
|
kind: QueryKind
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
* See `docs/engineering/reference/STAGES.md` § Stage 2.5 for the contract.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
export { classifyKind, classifyKindSync, createKindClassifier } from "
|
|
17
|
-
export type { KindClassifierOpts } from "
|
|
18
|
-
export { deriveIntentMarkers } from "
|
|
19
|
-
export type { IntentMarkerContext } from "
|
|
20
|
-
export { nearMeSubject, scoreBareToponym, scoreNearMe, scoreRoutePair } from "
|
|
21
|
-
export { matchPOICategory, matchPOISubject } from "
|
|
22
|
-
export type { POIPhraseMatch, POIPhraseLookup, POIQuerySpan, POISpatialRelation, POISubjectMatch } from "
|
|
16
|
+
export { classifyKind, classifyKindSync, createKindClassifier } from "#classify"
|
|
17
|
+
export type { KindClassifierOpts } from "#classify"
|
|
18
|
+
export { deriveIntentMarkers } from "#intent-markers"
|
|
19
|
+
export type { IntentMarkerContext } from "#intent-markers"
|
|
20
|
+
export { nearMeSubject, scoreBareToponym, scoreNearMe, scoreRoutePair } from "#intent-rules"
|
|
21
|
+
export { matchPOICategory, matchPOISubject } from "#poi"
|
|
22
|
+
export type { POIPhraseMatch, POIPhraseLookup, POIQuerySpan, POISpatialRelation, POISubjectMatch } from "#poi"
|
|
23
23
|
|
|
24
24
|
export {
|
|
25
25
|
scoreIntersection,
|
|
@@ -30,9 +30,9 @@ export {
|
|
|
30
30
|
scoreStructuredAddress,
|
|
31
31
|
scoreVague,
|
|
32
32
|
scoreVenueLandmark,
|
|
33
|
-
} from "
|
|
33
|
+
} from "#rules"
|
|
34
34
|
|
|
35
|
-
export { QueryIntentCode } from "
|
|
35
|
+
export { QueryIntentCode } from "#types"
|
|
36
36
|
|
|
37
37
|
export type {
|
|
38
38
|
LocaleHint,
|
|
@@ -41,4 +41,4 @@ export type {
|
|
|
41
41
|
QueryKind,
|
|
42
42
|
QueryKindResult,
|
|
43
43
|
QueryShapeLike,
|
|
44
|
-
} from "
|
|
44
|
+
} from "#types"
|
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
* Three of the four intent kinds can raise their marker here, from the string alone. The fourth —
|
|
10
10
|
* `bare_toponym`'s `declared_ambiguity` — cannot: its trigger is the dominance margin of the
|
|
11
11
|
* RESOLVED candidate list, which does not exist yet at Stage 2.5. That one is raised by
|
|
12
|
-
* `mailwoman/query-intent.ts` after the resolve, against the measured 0.5-log10
|
|
12
|
+
* `mailwoman/query-intent.ts` after the resolve, against the measured 0.5-log10 threshold. The split is
|
|
13
13
|
* deliberate and it is why this module never emits `declared_ambiguity`: a marker that asserted
|
|
14
14
|
* ambiguity from the string alone would be declaring that every bare city name is ambiguous, which
|
|
15
15
|
* is false 89.1% of the time (the measured table behind `DECISIVE_MARGIN_LOG10`).
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import { nearMeSubject } from "
|
|
19
|
-
import { matchPOICategory, type POIPhraseLookup } from "
|
|
20
|
-
import type { NormalizedInputLite, QueryIntentMarker, QueryKind } from "
|
|
18
|
+
import { nearMeSubject } from "#intent-rules"
|
|
19
|
+
import { matchPOICategory, type POIPhraseLookup } from "#poi"
|
|
20
|
+
import type { NormalizedInputLite, QueryIntentMarker, QueryKind } from "#types"
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Context the marker builder needs beyond the scored kinds.
|
|
@@ -50,6 +50,8 @@ export function deriveIntentMarkers(
|
|
|
50
50
|
const markers: QueryIntentMarker[] = []
|
|
51
51
|
|
|
52
52
|
if (fired.has("route_pair")) {
|
|
53
|
+
// Whitespace-only split, not `wordsOf`: `route_pair` inputs are comma-free by construction (a comma
|
|
54
|
+
// disqualifies the kind), and the tokens are re-joined verbatim into the message.
|
|
53
55
|
const tokens = ctx.input.normalized.trim().split(/\s+/)
|
|
54
56
|
|
|
55
57
|
markers.push({
|
|
@@ -79,9 +81,9 @@ export function deriveIntentMarkers(
|
|
|
79
81
|
evidence: {
|
|
80
82
|
subject,
|
|
81
83
|
/**
|
|
82
|
-
*
|
|
83
|
-
* `lat`/`lon` location-bias params — `photon/` is the eventual consumer of this marker, and this string
|
|
84
|
-
* note that says where it plugs in.
|
|
84
|
+
* THE PLUG POINT, named but not wired (ROAD_TO_V9 §4.4 scopes v9 to classification). Photon's `/api` already
|
|
85
|
+
* accepts `lat`/`lon` location-bias params — `photon/` is the eventual consumer of this marker, and this string
|
|
86
|
+
* is the note that says where it plugs in.
|
|
85
87
|
*/
|
|
86
88
|
focusParameter: "photon:lat/lon",
|
|
87
89
|
},
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
* ending "near me" is not a locality and answering it as one is the bug.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import { MAX_LOCALITY_ONLY_LENGTH,
|
|
27
|
-
import type { NormalizedInputLite, QueryShapeLike } from "
|
|
26
|
+
import { isDisqualifyingStreetSuffix, MAX_LOCALITY_ONLY_LENGTH, wordsOf } from "#rules"
|
|
27
|
+
import type { NormalizedInputLite, QueryShapeLike } from "#types"
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* `locality_only` scores 0.85. Both refinement kinds sit under it by a whole confidence step so no float-comparison
|
|
@@ -186,13 +186,6 @@ const DEICTIC_LOCATOR_TAIL =
|
|
|
186
186
|
const DEICTIC_ADVERB_TAIL =
|
|
187
187
|
/\b(?:nearby|near\s?by|close\s+by|around\s+here|in\s+my\s+(?:area|neighborhood|neighbourhood))\s*$/
|
|
188
188
|
|
|
189
|
-
/**
|
|
190
|
-
* Split on whitespace + commas, the way the other rules in this package do.
|
|
191
|
-
*/
|
|
192
|
-
function wordsOf(text: string): string[] {
|
|
193
|
-
return text.split(/[\s,]+/).filter(Boolean)
|
|
194
|
-
}
|
|
195
|
-
|
|
196
189
|
/**
|
|
197
190
|
* True when the input carries a deictic locator tail in EITHER form.
|
|
198
191
|
*/
|
|
@@ -201,12 +194,12 @@ function hasDeicticTail(lowercased: string): boolean {
|
|
|
201
194
|
}
|
|
202
195
|
|
|
203
196
|
/**
|
|
204
|
-
* The
|
|
197
|
+
* The conditions `bare_toponym` and `route_pair` share: no address grammar of any kind, one segment, alpha throughout.
|
|
205
198
|
*
|
|
206
199
|
* Returns the word list when the input clears them, `null` when it does not. Deliberately a SUPERSET of
|
|
207
|
-
* `scoreLocalityOnly`'s
|
|
208
|
-
* and can never fire where `locality_only` did not — the property `intent-rules.test.ts` asserts and
|
|
209
|
-
* ranking discipline above is enough to keep the top kind pinned.
|
|
200
|
+
* `scoreLocalityOnly`'s conditions (which admit two segments), so `bare_toponym` is a strict refinement of
|
|
201
|
+
* `locality_only` and can never fire where `locality_only` did not — the property `intent-rules.test.ts` asserts and
|
|
202
|
+
* the reason the ranking discipline above is enough to keep the top kind pinned.
|
|
210
203
|
*/
|
|
211
204
|
function bareNameWords(input: NormalizedInputLite, shape: QueryShapeLike): string[] | null {
|
|
212
205
|
const text = input.normalized.trim()
|
|
@@ -232,7 +225,7 @@ function bareNameWords(input: NormalizedInputLite, shape: QueryShapeLike): strin
|
|
|
232
225
|
if (!words.length || words.length > MAX_BARE_TOPONYM_WORDS) return null
|
|
233
226
|
|
|
234
227
|
for (const word of words) {
|
|
235
|
-
if (
|
|
228
|
+
if (isDisqualifyingStreetSuffix(word)) return null
|
|
236
229
|
}
|
|
237
230
|
|
|
238
231
|
return words
|
package/{poi.ts → lib/poi.ts}
RENAMED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* `poiQueryKind` flag (default-ON since 2026-07-20). Spec §3.1.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import type { NormalizedInputLite, QueryShapeLike } from "
|
|
12
|
+
import type { NormalizedInputLite, QueryShapeLike } from "#types"
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Comma-segment ceiling for a POI-led query. Past it the input is a venue plus a full address (`X, 350 5th Ave, New
|
|
@@ -40,6 +40,31 @@ export interface POIPhraseMatch {
|
|
|
40
40
|
* Wikidata QID, when known. `kind: "brand"` only — absent when a brand resolved by name alone (no QID match).
|
|
41
41
|
*/
|
|
42
42
|
wikidata?: string
|
|
43
|
+
/**
|
|
44
|
+
* Whether this hit is one member of a set the caller must search TOGETHER, rather than one candidate in a preference
|
|
45
|
+
* list.
|
|
46
|
+
*
|
|
47
|
+
* A lookup returning several hits means two different things, and the difference decides whether narrowing to the
|
|
48
|
+
* first is an answer or an invented ordering. A phrase index returns the categories one typed phrase could name, the
|
|
49
|
+
* curated reading first (`credit union` → the `bank` rollup its synonym redirects to, then the standalone
|
|
50
|
+
* `credit_union` category), and the first entry IS the subject. An affordance rung returns every entity kind that
|
|
51
|
+
* affords ONE activity, in a stable enumeration that is not a preference, and taking the first picks a winner nobody
|
|
52
|
+
* authored.
|
|
53
|
+
*
|
|
54
|
+
* Set on every member of such a set. {@link matchPOISubject} then carries them all, the POI branch searches their
|
|
55
|
+
* union, and the candidate ordering the resolver already owns decides the answer. Absent — the committed lexicon's
|
|
56
|
+
* shape — keeps the first-hit reading unchanged.
|
|
57
|
+
*/
|
|
58
|
+
searchAsSet?: boolean
|
|
59
|
+
/**
|
|
60
|
+
* ISO 3166-1 alpha-2 countries the authority behind this hit scopes its claim to. Absent = the claim holds
|
|
61
|
+
* everywhere.
|
|
62
|
+
*
|
|
63
|
+
* A scope is a statement about ESTABLISHMENTS, so it is judged against the country of the place being searched, not
|
|
64
|
+
* the caller's locale: the locale is the lens the phrase is read through, and it says nothing about where the claim
|
|
65
|
+
* holds. `matchPOISubject` carries the value untouched; the POI intent stage binds it once the anchor has resolved.
|
|
66
|
+
*/
|
|
67
|
+
countryScope?: readonly string[]
|
|
43
68
|
}
|
|
44
69
|
|
|
45
70
|
/**
|
|
@@ -62,7 +87,19 @@ export interface POIQuerySpan {
|
|
|
62
87
|
* Which lexicon this hit came from. Existing category lookups set `"category"` (backward-compatible default).
|
|
63
88
|
*/
|
|
64
89
|
export interface POISubjectMatch {
|
|
90
|
+
/**
|
|
91
|
+
* The hit the subject SCORES under — its kind and its confidence. Always `matches[0]`; the two are built together in
|
|
92
|
+
* one place so they cannot disagree.
|
|
93
|
+
*/
|
|
65
94
|
match: POIPhraseMatch
|
|
95
|
+
/**
|
|
96
|
+
* Every category the subject reaches, `match` first.
|
|
97
|
+
*
|
|
98
|
+
* One entry unless the lookup returned a {@link POIPhraseMatch.searchAsSet} set, in which case it holds the whole set
|
|
99
|
+
* and the POI branch searches their union. The order is the order the lookup returned and states no preference:
|
|
100
|
+
* nothing downstream may read position as rank.
|
|
101
|
+
*/
|
|
102
|
+
matches: POIPhraseMatch[]
|
|
66
103
|
/**
|
|
67
104
|
* The matched subject text as it appeared in the query.
|
|
68
105
|
*/
|
|
@@ -105,11 +142,26 @@ const ANCHOR_SEPARATOR = /,\s*|\s(near|in|at|around|to)\s+/gi
|
|
|
105
142
|
*/
|
|
106
143
|
const MAX_SUBJECT_TOKENS = 8
|
|
107
144
|
|
|
145
|
+
/**
|
|
146
|
+
* The categories one candidate subject reaches, from the hits the lookup returned for it.
|
|
147
|
+
*
|
|
148
|
+
* The whole array when the first hit declares {@link POIPhraseMatch.searchAsSet} — carried as the lookup returned it,
|
|
149
|
+
* never filtered, so a rung that flagged only some of its members loses nothing here and the inconsistency stays
|
|
150
|
+
* visible to whoever reads the set. Otherwise the first hit alone, which is the preference-list reading the committed
|
|
151
|
+
* phrase index has always had.
|
|
152
|
+
*/
|
|
153
|
+
function reachedMatches(hits: ReadonlyArray<POIPhraseMatch>): POIPhraseMatch[] {
|
|
154
|
+
return hits[0]!.searchAsSet ? [...hits] : [hits[0]!]
|
|
155
|
+
}
|
|
156
|
+
|
|
108
157
|
/**
|
|
109
158
|
* Match a POI subject: the whole input, or the text before the FIRST anchor separator WHOSE PREFIX HITS THE LEXICON (≤
|
|
110
159
|
* 8 tokens). Scans separator occurrences left-to-right — a lexicon phrase may itself contain a bare separator word
|
|
111
160
|
* (e.g. "walk in clinic"), so the first separator isn't necessarily the right split point. Returns null when the
|
|
112
161
|
* lexicon never fires — including comma-ridden full addresses whose leading segment isn't a lexicon phrase.
|
|
162
|
+
*
|
|
163
|
+
* The winning candidate's hits are carried per {@link reachedMatches}: the first hit, or the whole set when the lookup
|
|
164
|
+
* declared one.
|
|
113
165
|
*/
|
|
114
166
|
export function matchPOISubject(
|
|
115
167
|
text: string,
|
|
@@ -124,8 +176,11 @@ export function matchPOISubject(
|
|
|
124
176
|
const whole = lookup(trimmed, locale)
|
|
125
177
|
|
|
126
178
|
if (whole.length) {
|
|
179
|
+
const matches = reachedMatches(whole)
|
|
180
|
+
|
|
127
181
|
return {
|
|
128
|
-
match:
|
|
182
|
+
match: matches[0]!,
|
|
183
|
+
matches,
|
|
129
184
|
subject: trimmed,
|
|
130
185
|
subjectSpan: { text: trimmed, start: inputStart, end: inputStart + trimmed.length },
|
|
131
186
|
remainder: "",
|
|
@@ -137,7 +192,8 @@ export function matchPOISubject(
|
|
|
137
192
|
|
|
138
193
|
const subject = trimmed.slice(0, separator.index).trim()
|
|
139
194
|
|
|
140
|
-
// Subjects only grow as the scan moves right — once over budget, later splits are too.
|
|
195
|
+
// Subjects only grow as the scan moves right — once over budget, later splits are too. Whitespace-only
|
|
196
|
+
// split, not `wordsOf`: a comma inside a subject is real content here, not a separator to erase.
|
|
141
197
|
if (subject.split(/\s+/).length > MAX_SUBJECT_TOKENS) break
|
|
142
198
|
|
|
143
199
|
const hits = lookup(subject, locale)
|
|
@@ -152,9 +208,11 @@ export function matchPOISubject(
|
|
|
152
208
|
const anchorOffset = trimmed.indexOf(remainder, separator.index + separator[0].length)
|
|
153
209
|
const relationText = separator[1] ?? ","
|
|
154
210
|
const relationOffset = separator[1] ? separator.index + separator[0].indexOf(separator[1]) : separator.index
|
|
211
|
+
const matches = reachedMatches(hits)
|
|
155
212
|
|
|
156
213
|
return {
|
|
157
|
-
match:
|
|
214
|
+
match: matches[0]!,
|
|
215
|
+
matches,
|
|
158
216
|
subject,
|
|
159
217
|
subjectSpan: {
|
|
160
218
|
text: subject,
|
|
@@ -241,7 +299,7 @@ export function createScorePOICategory(
|
|
|
241
299
|
|
|
242
300
|
/**
|
|
243
301
|
* The whole-input category hit behind a `poi_category` verdict, for the marker's evidence. `null` when the input is not
|
|
244
|
-
* a bare category — same
|
|
302
|
+
* a bare category — same conditions as {@link createScorePOICategory}, so the two cannot disagree.
|
|
245
303
|
*/
|
|
246
304
|
export function matchPOICategory(
|
|
247
305
|
text: string,
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
* regex set per new locale, not 50K dictionary entries.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import
|
|
13
|
+
import { NAME_PRONE_US_SUFFIXES, US_STREET_SUFFIX_LOOKUP } from "@mailwoman/codex/us/street-suffix"
|
|
14
|
+
|
|
15
|
+
import type { NormalizedInputLite, QueryShapeLike } from "#types"
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* Longest input still plausible as a bare venue or landmark name. Beyond it the query is carrying an address as well,
|
|
@@ -128,34 +130,25 @@ export function scoreLandmark(input: NormalizedInputLite, _shape: QueryShapeLike
|
|
|
128
130
|
}
|
|
129
131
|
|
|
130
132
|
/**
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
+
* Split on whitespace + commas and drop empty words — the word grammar shared with `intent-rules.ts`. Dropping empties
|
|
134
|
+
* matters: a trailing comma or doubled separator otherwise yields an empty string that inflates the word count and
|
|
135
|
+
* falsifies every-word predicates like the proper-case check below.
|
|
133
136
|
*/
|
|
134
|
-
export
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
"pl",
|
|
150
|
-
"place",
|
|
151
|
-
"way",
|
|
152
|
-
"pkwy",
|
|
153
|
-
"parkway",
|
|
154
|
-
"hwy",
|
|
155
|
-
"highway",
|
|
156
|
-
"cir",
|
|
157
|
-
"circle",
|
|
158
|
-
])
|
|
137
|
+
export function wordsOf(text: string): string[] {
|
|
138
|
+
return text.split(/[\s,]+/).filter((word) => word.length > 0)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* True when a word is USPS street-suffix vocabulary that unambiguously signals an address — the full Pub-28 table via
|
|
143
|
+
* `@mailwoman/codex`, minus its curated name-prone canonicals (PARK, FIELD, HILL, LAKE, …). Those double as ordinary
|
|
144
|
+
* proper-name heads ("Wrigley Field", "Menlo Park"), and disqualifying on them would reject the very venue and place
|
|
145
|
+
* names the rules below exist to capture. Shared with `intent-rules.ts` so both rule sets read one definition.
|
|
146
|
+
*/
|
|
147
|
+
export function isDisqualifyingStreetSuffix(word: string): boolean {
|
|
148
|
+
const canonical = US_STREET_SUFFIX_LOOKUP.get(word.trim().toLowerCase())
|
|
149
|
+
|
|
150
|
+
return canonical !== undefined && !NAME_PRONE_US_SUFFIXES.has(canonical)
|
|
151
|
+
}
|
|
159
152
|
|
|
160
153
|
/**
|
|
161
154
|
* `landmark` rule (venue/named-place variant): short capitalized input with no street suffixes, no postcode hits, and
|
|
@@ -181,11 +174,11 @@ export function scoreVenueLandmark(input: NormalizedInputLite, shape: QueryShape
|
|
|
181
174
|
|
|
182
175
|
if (segCount > 2) return 0
|
|
183
176
|
|
|
184
|
-
// Reject if any word is
|
|
185
|
-
const words = text
|
|
177
|
+
// Reject if any word is an unambiguous street suffix.
|
|
178
|
+
const words = wordsOf(text)
|
|
186
179
|
|
|
187
180
|
for (const w of words) {
|
|
188
|
-
if (
|
|
181
|
+
if (isDisqualifyingStreetSuffix(w)) return 0
|
|
189
182
|
}
|
|
190
183
|
|
|
191
184
|
// Reject if the first token is a pure number (house-number-leading pattern).
|
package/lib/types.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Re-exports of the canonical types from `@mailwoman/core/pipeline`.
|
|
9
|
+
*/
|
|
10
|
+
export type { LocaleHint, QueryIntentMarker, QueryKind, QueryKindResult } from "@mailwoman/core/pipeline"
|
|
11
|
+
export { QueryIntentCode } from "@mailwoman/core/pipeline"
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The minimal input shapes consumed by `classifyKind` come from `@mailwoman/query-shape` — one declaration shared by
|
|
15
|
+
* every Stage-2.x consumer. `QueryShapeLike` stays this package's public name for its narrow read-only view.
|
|
16
|
+
*/
|
|
17
|
+
export type { NormalizedInputLite, QueryShapeSegmentsView as QueryShapeLike } from "@mailwoman/query-shape"
|
package/out/classify.d.ts
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
* are scored below their structural incumbent precisely so they land in `alternatives`, where they
|
|
16
16
|
* inform the markers without moving the routing decision.
|
|
17
17
|
*/
|
|
18
|
-
import { type POIPhraseLookup } from "
|
|
19
|
-
import type { LocaleHint, NormalizedInputLite, QueryKindResult, QueryShapeLike } from "
|
|
18
|
+
import { type POIPhraseLookup } from "#poi";
|
|
19
|
+
import type { LocaleHint, NormalizedInputLite, QueryKindResult, QueryShapeLike } from "#types";
|
|
20
20
|
/**
|
|
21
21
|
* Classify the query shape into a `QueryKind`. Synchronous + pure — produces the same result for the same `(input,
|
|
22
22
|
* shape)` pair.
|
package/out/classify.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../classify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,EAA+C,KAAK,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../lib/classify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,EAA+C,KAAK,eAAe,EAAE,MAAM,MAAM,CAAA;AAWxF,OAAO,KAAK,EACX,UAAU,EACV,mBAAmB,EAGnB,eAAe,EACf,cAAc,EACd,MAAM,QAAQ,CAAA;AAkEf;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,eAAe,CAMnG;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CACjC,KAAK,EAAE,mBAAmB,EAC1B,KAAK,EAAE,cAAc,EACrB,OAAO,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC,eAAe,CAAC,CAE1B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC;;;;OAIG;IACH,UAAU,CAAC,EAAE,eAAe,CAAA;CAC5B;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CACnC,IAAI,GAAE,kBAAuB,GAC3B,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,UAAU,KAAK,OAAO,CAAC,eAAe,CAAC,CA+BtG"}
|
package/out/classify.js
CHANGED
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
* are scored below their structural incumbent precisely so they land in `alternatives`, where they
|
|
16
16
|
* inform the markers without moving the routing decision.
|
|
17
17
|
*/
|
|
18
|
-
import { deriveIntentMarkers } from "
|
|
19
|
-
import { scoreBareToponym, scoreNearMe, scoreRoutePair } from "
|
|
20
|
-
import { createScorePOICategory, createScorePOIQuery } from "
|
|
21
|
-
import { scoreIntersection, scoreLandmark, scoreLocalityOnly, scorePoBox, scorePostcodeOnly, scoreStructuredAddress, scoreVague, scoreVenueLandmark, } from "
|
|
18
|
+
import { deriveIntentMarkers } from "#intent-markers";
|
|
19
|
+
import { scoreBareToponym, scoreNearMe, scoreRoutePair } from "#intent-rules";
|
|
20
|
+
import { createScorePOICategory, createScorePOIQuery } from "#poi";
|
|
21
|
+
import { scoreIntersection, scoreLandmark, scoreLocalityOnly, scorePoBox, scorePostcodeOnly, scoreStructuredAddress, scoreVague, scoreVenueLandmark, } from "#rules";
|
|
22
22
|
const SCORERS = [
|
|
23
23
|
{ kind: "po_box", score: scorePoBox },
|
|
24
24
|
{ kind: "landmark", score: (i, s) => Math.max(scoreLandmark(i, s), scoreVenueLandmark(i, s)) },
|
package/out/classify.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../classify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../lib/classify.ts"],"names":[],"mappings":"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"}
|
package/out/index.d.ts
CHANGED
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
*
|
|
13
13
|
* See `docs/engineering/reference/STAGES.md` § Stage 2.5 for the contract.
|
|
14
14
|
*/
|
|
15
|
-
export { classifyKind, classifyKindSync, createKindClassifier } from "
|
|
16
|
-
export type { KindClassifierOpts } from "
|
|
17
|
-
export { deriveIntentMarkers } from "
|
|
18
|
-
export type { IntentMarkerContext } from "
|
|
19
|
-
export { nearMeSubject, scoreBareToponym, scoreNearMe, scoreRoutePair } from "
|
|
20
|
-
export { matchPOICategory, matchPOISubject } from "
|
|
21
|
-
export type { POIPhraseMatch, POIPhraseLookup, POIQuerySpan, POISpatialRelation, POISubjectMatch } from "
|
|
22
|
-
export { scoreIntersection, scoreLandmark, scoreLocalityOnly, scorePoBox, scorePostcodeOnly, scoreStructuredAddress, scoreVague, scoreVenueLandmark, } from "
|
|
23
|
-
export { QueryIntentCode } from "
|
|
24
|
-
export type { LocaleHint, NormalizedInputLite, QueryIntentMarker, QueryKind, QueryKindResult, QueryShapeLike, } from "
|
|
15
|
+
export { classifyKind, classifyKindSync, createKindClassifier } from "#classify";
|
|
16
|
+
export type { KindClassifierOpts } from "#classify";
|
|
17
|
+
export { deriveIntentMarkers } from "#intent-markers";
|
|
18
|
+
export type { IntentMarkerContext } from "#intent-markers";
|
|
19
|
+
export { nearMeSubject, scoreBareToponym, scoreNearMe, scoreRoutePair } from "#intent-rules";
|
|
20
|
+
export { matchPOICategory, matchPOISubject } from "#poi";
|
|
21
|
+
export type { POIPhraseMatch, POIPhraseLookup, POIQuerySpan, POISpatialRelation, POISubjectMatch } from "#poi";
|
|
22
|
+
export { scoreIntersection, scoreLandmark, scoreLocalityOnly, scorePoBox, scorePostcodeOnly, scoreStructuredAddress, scoreVague, scoreVenueLandmark, } from "#rules";
|
|
23
|
+
export { QueryIntentCode } from "#types";
|
|
24
|
+
export type { LocaleHint, NormalizedInputLite, QueryIntentMarker, QueryKind, QueryKindResult, QueryShapeLike, } from "#types";
|
|
25
25
|
//# sourceMappingURL=index.d.ts.map
|
package/out/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAChF,YAAY,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,YAAY,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAC1D,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC5F,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AACxD,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AAE9G,OAAO,EACN,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,iBAAiB,EACjB,sBAAsB,EACtB,UAAU,EACV,kBAAkB,GAClB,MAAM,QAAQ,CAAA;AAEf,OAAO,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAA;AAExC,YAAY,EACX,UAAU,EACV,mBAAmB,EACnB,iBAAiB,EACjB,SAAS,EACT,eAAe,EACf,cAAc,GACd,MAAM,QAAQ,CAAA"}
|
package/out/index.js
CHANGED
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
*
|
|
13
13
|
* See `docs/engineering/reference/STAGES.md` § Stage 2.5 for the contract.
|
|
14
14
|
*/
|
|
15
|
-
export { classifyKind, classifyKindSync, createKindClassifier } from "
|
|
16
|
-
export { deriveIntentMarkers } from "
|
|
17
|
-
export { nearMeSubject, scoreBareToponym, scoreNearMe, scoreRoutePair } from "
|
|
18
|
-
export { matchPOICategory, matchPOISubject } from "
|
|
19
|
-
export { scoreIntersection, scoreLandmark, scoreLocalityOnly, scorePoBox, scorePostcodeOnly, scoreStructuredAddress, scoreVague, scoreVenueLandmark, } from "
|
|
20
|
-
export { QueryIntentCode } from "
|
|
15
|
+
export { classifyKind, classifyKindSync, createKindClassifier } from "#classify";
|
|
16
|
+
export { deriveIntentMarkers } from "#intent-markers";
|
|
17
|
+
export { nearMeSubject, scoreBareToponym, scoreNearMe, scoreRoutePair } from "#intent-rules";
|
|
18
|
+
export { matchPOICategory, matchPOISubject } from "#poi";
|
|
19
|
+
export { scoreIntersection, scoreLandmark, scoreLocalityOnly, scorePoBox, scorePostcodeOnly, scoreStructuredAddress, scoreVague, scoreVenueLandmark, } from "#rules";
|
|
20
|
+
export { QueryIntentCode } from "#types";
|
|
21
21
|
//# sourceMappingURL=index.js.map
|
package/out/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAEhF,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAErD,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC5F,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AAGxD,OAAO,EACN,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,iBAAiB,EACjB,sBAAsB,EACtB,UAAU,EACV,kBAAkB,GAClB,MAAM,QAAQ,CAAA;AAEf,OAAO,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAA"}
|
package/out/intent-markers.d.ts
CHANGED
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
* Three of the four intent kinds can raise their marker here, from the string alone. The fourth —
|
|
10
10
|
* `bare_toponym`'s `declared_ambiguity` — cannot: its trigger is the dominance margin of the
|
|
11
11
|
* RESOLVED candidate list, which does not exist yet at Stage 2.5. That one is raised by
|
|
12
|
-
* `mailwoman/query-intent.ts` after the resolve, against the measured 0.5-log10
|
|
12
|
+
* `mailwoman/query-intent.ts` after the resolve, against the measured 0.5-log10 threshold. The split is
|
|
13
13
|
* deliberate and it is why this module never emits `declared_ambiguity`: a marker that asserted
|
|
14
14
|
* ambiguity from the string alone would be declaring that every bare city name is ambiguous, which
|
|
15
15
|
* is false 89.1% of the time (the measured table behind `DECISIVE_MARGIN_LOG10`).
|
|
16
16
|
*/
|
|
17
|
-
import { type POIPhraseLookup } from "
|
|
18
|
-
import type { NormalizedInputLite, QueryIntentMarker, QueryKind } from "
|
|
17
|
+
import { type POIPhraseLookup } from "#poi";
|
|
18
|
+
import type { NormalizedInputLite, QueryIntentMarker, QueryKind } from "#types";
|
|
19
19
|
/**
|
|
20
20
|
* Context the marker builder needs beyond the scored kinds.
|
|
21
21
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent-markers.d.ts","sourceRoot":"","sources":["../intent-markers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"intent-markers.d.ts","sourceRoot":"","sources":["../lib/intent-markers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,MAAM,CAAA;AAC7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAE/E;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,KAAK,EAAE,mBAAmB,CAAA;IAC1B;;;OAGG;IACH,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAClC,KAAK,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,EAC7D,GAAG,EAAE,mBAAmB,GACtB,iBAAiB,EAAE,CAgErB"}
|
package/out/intent-markers.js
CHANGED
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
* Three of the four intent kinds can raise their marker here, from the string alone. The fourth —
|
|
10
10
|
* `bare_toponym`'s `declared_ambiguity` — cannot: its trigger is the dominance margin of the
|
|
11
11
|
* RESOLVED candidate list, which does not exist yet at Stage 2.5. That one is raised by
|
|
12
|
-
* `mailwoman/query-intent.ts` after the resolve, against the measured 0.5-log10
|
|
12
|
+
* `mailwoman/query-intent.ts` after the resolve, against the measured 0.5-log10 threshold. The split is
|
|
13
13
|
* deliberate and it is why this module never emits `declared_ambiguity`: a marker that asserted
|
|
14
14
|
* ambiguity from the string alone would be declaring that every bare city name is ambiguous, which
|
|
15
15
|
* is false 89.1% of the time (the measured table behind `DECISIVE_MARGIN_LOG10`).
|
|
16
16
|
*/
|
|
17
|
-
import { nearMeSubject } from "
|
|
18
|
-
import { matchPOICategory } from "
|
|
17
|
+
import { nearMeSubject } from "#intent-rules";
|
|
18
|
+
import { matchPOICategory } from "#poi";
|
|
19
19
|
/**
|
|
20
20
|
* Build the advisories for one classified query.
|
|
21
21
|
*
|
|
@@ -30,6 +30,8 @@ export function deriveIntentMarkers(kinds, ctx) {
|
|
|
30
30
|
const fired = new Set(kinds.map((k) => k.kind));
|
|
31
31
|
const markers = [];
|
|
32
32
|
if (fired.has("route_pair")) {
|
|
33
|
+
// Whitespace-only split, not `wordsOf`: `route_pair` inputs are comma-free by construction (a comma
|
|
34
|
+
// disqualifies the kind), and the tokens are re-joined verbatim into the message.
|
|
33
35
|
const tokens = ctx.input.normalized.trim().split(/\s+/);
|
|
34
36
|
markers.push({
|
|
35
37
|
kind: "route_pair",
|
|
@@ -56,9 +58,9 @@ export function deriveIntentMarkers(kinds, ctx) {
|
|
|
56
58
|
evidence: {
|
|
57
59
|
subject,
|
|
58
60
|
/**
|
|
59
|
-
*
|
|
60
|
-
* `lat`/`lon` location-bias params — `photon/` is the eventual consumer of this marker, and this string
|
|
61
|
-
* note that says where it plugs in.
|
|
61
|
+
* THE PLUG POINT, named but not wired (ROAD_TO_V9 §4.4 scopes v9 to classification). Photon's `/api` already
|
|
62
|
+
* accepts `lat`/`lon` location-bias params — `photon/` is the eventual consumer of this marker, and this string
|
|
63
|
+
* is the note that says where it plugs in.
|
|
62
64
|
*/
|
|
63
65
|
focusParameter: "photon:lat/lon",
|
|
64
66
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent-markers.js","sourceRoot":"","sources":["../intent-markers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"intent-markers.js","sourceRoot":"","sources":["../lib/intent-markers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAwB,MAAM,MAAM,CAAA;AAgB7D;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAClC,KAA6D,EAC7D,GAAwB;IAExB,MAAM,KAAK,GAAG,IAAI,GAAG,CAAY,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;IAC1D,MAAM,OAAO,GAAwB,EAAE,CAAA;IAEvC,IAAI,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7B,oGAAoG;QACpG,kFAAkF;QAClF,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QAEvD,OAAO,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,YAAY;YAClB,IAAI,EAAE,eAAe;YACrB,SAAS,EAAE,iBAAiB;YAC5B,OAAO,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,2HAA2H;YACxJ,QAAQ,EAAE;gBACT,MAAM;gBACN;;;mBAGG;gBACH,eAAe,EAAE,CAAC,cAAc,EAAE,6BAA6B,CAAC;aAChE;SACD,CAAC,CAAA;IACH,CAAC;IAED,IAAI,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAExC,OAAO,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,sBAAsB;YAC5B,SAAS,EAAE,cAAc;YACzB,OAAO,EAAE,IAAI,OAAO,yEAAyE;YAC7F,QAAQ,EAAE;gBACT,OAAO;gBACP;;;;mBAIG;gBACH,cAAc,EAAE,gBAAgB;aAChC;SACD,CAAC,CAAA;IACH,CAAC;IAED,IAAI,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;QACjD,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,CAAC,UAAU,CAAC,CAAA;QAE3G,IAAI,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,cAAc;gBACpB,IAAI,EAAE,cAAc;gBACpB,SAAS,EAAE,sBAAsB;gBACjC,OAAO,EAAE,IAAI,KAAK,CAAC,aAAa,yFAAyF;gBACzH,QAAQ,EAAE;oBACT,UAAU,EAAE,KAAK,CAAC,UAAU;oBAC5B,aAAa,EAAE,KAAK,CAAC,aAAa;oBAClC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACvD;aACD,CAAC,CAAA;QACH,CAAC;IACF,CAAC;IAED,OAAO,OAAO,CAAA;AACf,CAAC"}
|
package/out/intent-rules.d.ts
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* `near_me` DOES win its top slot (0.91), because there is no incumbent worth preserving: a query
|
|
23
23
|
* ending "near me" is not a locality and answering it as one is the bug.
|
|
24
24
|
*/
|
|
25
|
-
import type { NormalizedInputLite, QueryShapeLike } from "
|
|
25
|
+
import type { NormalizedInputLite, QueryShapeLike } from "#types";
|
|
26
26
|
/**
|
|
27
27
|
* `bare_toponym` rule: a single coherent place-name carrying no address grammar.
|
|
28
28
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent-rules.d.ts","sourceRoot":"","sources":["../intent-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"intent-rules.d.ts","sourceRoot":"","sources":["../lib/intent-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AA+MjE;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAE1F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAgBxF;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,mBAAmB,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAYtF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,mBAAmB,GAAG,MAAM,CAQhE"}
|
package/out/intent-rules.js
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* `near_me` DOES win its top slot (0.91), because there is no incumbent worth preserving: a query
|
|
23
23
|
* ending "near me" is not a locality and answering it as one is the bug.
|
|
24
24
|
*/
|
|
25
|
-
import { MAX_LOCALITY_ONLY_LENGTH,
|
|
25
|
+
import { isDisqualifyingStreetSuffix, MAX_LOCALITY_ONLY_LENGTH, wordsOf } from "#rules";
|
|
26
26
|
/**
|
|
27
27
|
* `locality_only` scores 0.85. Both refinement kinds sit under it by a whole confidence step so no float-comparison
|
|
28
28
|
* accident can flip the top slot, and so the gap reads as deliberate to the next person.
|
|
@@ -173,12 +173,6 @@ const DEICTIC_LOCATOR_TAIL = /\b(?:near|close\s+to|next\s+to|around|by|closest\s
|
|
|
173
173
|
* The adverbial half of the same class — no preposition, the deixis is baked into the word.
|
|
174
174
|
*/
|
|
175
175
|
const DEICTIC_ADVERB_TAIL = /\b(?:nearby|near\s?by|close\s+by|around\s+here|in\s+my\s+(?:area|neighborhood|neighbourhood))\s*$/;
|
|
176
|
-
/**
|
|
177
|
-
* Split on whitespace + commas, the way the other rules in this package do.
|
|
178
|
-
*/
|
|
179
|
-
function wordsOf(text) {
|
|
180
|
-
return text.split(/[\s,]+/).filter(Boolean);
|
|
181
|
-
}
|
|
182
176
|
/**
|
|
183
177
|
* True when the input carries a deictic locator tail in EITHER form.
|
|
184
178
|
*/
|
|
@@ -186,12 +180,12 @@ function hasDeicticTail(lowercased) {
|
|
|
186
180
|
return DEICTIC_LOCATOR_TAIL.test(lowercased) || DEICTIC_ADVERB_TAIL.test(lowercased);
|
|
187
181
|
}
|
|
188
182
|
/**
|
|
189
|
-
* The
|
|
183
|
+
* The conditions `bare_toponym` and `route_pair` share: no address grammar of any kind, one segment, alpha throughout.
|
|
190
184
|
*
|
|
191
185
|
* Returns the word list when the input clears them, `null` when it does not. Deliberately a SUPERSET of
|
|
192
|
-
* `scoreLocalityOnly`'s
|
|
193
|
-
* and can never fire where `locality_only` did not — the property `intent-rules.test.ts` asserts and
|
|
194
|
-
* ranking discipline above is enough to keep the top kind pinned.
|
|
186
|
+
* `scoreLocalityOnly`'s conditions (which admit two segments), so `bare_toponym` is a strict refinement of
|
|
187
|
+
* `locality_only` and can never fire where `locality_only` did not — the property `intent-rules.test.ts` asserts and
|
|
188
|
+
* the reason the ranking discipline above is enough to keep the top kind pinned.
|
|
195
189
|
*/
|
|
196
190
|
function bareNameWords(input, shape) {
|
|
197
191
|
const text = input.normalized.trim();
|
|
@@ -214,7 +208,7 @@ function bareNameWords(input, shape) {
|
|
|
214
208
|
if (!words.length || words.length > MAX_BARE_TOPONYM_WORDS)
|
|
215
209
|
return null;
|
|
216
210
|
for (const word of words) {
|
|
217
|
-
if (
|
|
211
|
+
if (isDisqualifyingStreetSuffix(word))
|
|
218
212
|
return null;
|
|
219
213
|
}
|
|
220
214
|
return words;
|
package/out/intent-rules.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent-rules.js","sourceRoot":"","sources":["../intent-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,wBAAwB,EAAE,
|
|
1
|
+
{"version":3,"file":"intent-rules.js","sourceRoot":"","sources":["../lib/intent-rules.ts"],"names":[],"mappings":"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"}
|
package/out/poi.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* lives in `@mailwoman/poi-taxonomy` and is wired in by `createRuntimePipeline` behind the
|
|
9
9
|
* `poiQueryKind` flag (default-ON since 2026-07-20). Spec §3.1.
|
|
10
10
|
*/
|
|
11
|
-
import type { NormalizedInputLite, QueryShapeLike } from "
|
|
11
|
+
import type { NormalizedInputLite, QueryShapeLike } from "#types";
|
|
12
12
|
/**
|
|
13
13
|
* One lexicon hit for a candidate subject phrase.
|
|
14
14
|
*/
|
|
@@ -32,6 +32,31 @@ export interface POIPhraseMatch {
|
|
|
32
32
|
* Wikidata QID, when known. `kind: "brand"` only — absent when a brand resolved by name alone (no QID match).
|
|
33
33
|
*/
|
|
34
34
|
wikidata?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Whether this hit is one member of a set the caller must search TOGETHER, rather than one candidate in a preference
|
|
37
|
+
* list.
|
|
38
|
+
*
|
|
39
|
+
* A lookup returning several hits means two different things, and the difference decides whether narrowing to the
|
|
40
|
+
* first is an answer or an invented ordering. A phrase index returns the categories one typed phrase could name, the
|
|
41
|
+
* curated reading first (`credit union` → the `bank` rollup its synonym redirects to, then the standalone
|
|
42
|
+
* `credit_union` category), and the first entry IS the subject. An affordance rung returns every entity kind that
|
|
43
|
+
* affords ONE activity, in a stable enumeration that is not a preference, and taking the first picks a winner nobody
|
|
44
|
+
* authored.
|
|
45
|
+
*
|
|
46
|
+
* Set on every member of such a set. {@link matchPOISubject} then carries them all, the POI branch searches their
|
|
47
|
+
* union, and the candidate ordering the resolver already owns decides the answer. Absent — the committed lexicon's
|
|
48
|
+
* shape — keeps the first-hit reading unchanged.
|
|
49
|
+
*/
|
|
50
|
+
searchAsSet?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* ISO 3166-1 alpha-2 countries the authority behind this hit scopes its claim to. Absent = the claim holds
|
|
53
|
+
* everywhere.
|
|
54
|
+
*
|
|
55
|
+
* A scope is a statement about ESTABLISHMENTS, so it is judged against the country of the place being searched, not
|
|
56
|
+
* the caller's locale: the locale is the lens the phrase is read through, and it says nothing about where the claim
|
|
57
|
+
* holds. `matchPOISubject` carries the value untouched; the POI intent stage binds it once the anchor has resolved.
|
|
58
|
+
*/
|
|
59
|
+
countryScope?: readonly string[];
|
|
35
60
|
}
|
|
36
61
|
/**
|
|
37
62
|
* Injected phrase→category lookup. Exact-phrase, locale-aware; returns [] on miss.
|
|
@@ -50,7 +75,19 @@ export interface POIQuerySpan {
|
|
|
50
75
|
* Which lexicon this hit came from. Existing category lookups set `"category"` (backward-compatible default).
|
|
51
76
|
*/
|
|
52
77
|
export interface POISubjectMatch {
|
|
78
|
+
/**
|
|
79
|
+
* The hit the subject SCORES under — its kind and its confidence. Always `matches[0]`; the two are built together in
|
|
80
|
+
* one place so they cannot disagree.
|
|
81
|
+
*/
|
|
53
82
|
match: POIPhraseMatch;
|
|
83
|
+
/**
|
|
84
|
+
* Every category the subject reaches, `match` first.
|
|
85
|
+
*
|
|
86
|
+
* One entry unless the lookup returned a {@link POIPhraseMatch.searchAsSet} set, in which case it holds the whole set
|
|
87
|
+
* and the POI branch searches their union. The order is the order the lookup returned and states no preference:
|
|
88
|
+
* nothing downstream may read position as rank.
|
|
89
|
+
*/
|
|
90
|
+
matches: POIPhraseMatch[];
|
|
54
91
|
/**
|
|
55
92
|
* The matched subject text as it appeared in the query.
|
|
56
93
|
*/
|
|
@@ -72,6 +109,9 @@ export interface POISubjectMatch {
|
|
|
72
109
|
* 8 tokens). Scans separator occurrences left-to-right — a lexicon phrase may itself contain a bare separator word
|
|
73
110
|
* (e.g. "walk in clinic"), so the first separator isn't necessarily the right split point. Returns null when the
|
|
74
111
|
* lexicon never fires — including comma-ridden full addresses whose leading segment isn't a lexicon phrase.
|
|
112
|
+
*
|
|
113
|
+
* The winning candidate's hits are carried per {@link reachedMatches}: the first hit, or the whole set when the lookup
|
|
114
|
+
* declared one.
|
|
75
115
|
*/
|
|
76
116
|
export declare function matchPOISubject(text: string, locale: string | undefined, lookup: POIPhraseLookup): POISubjectMatch | null;
|
|
77
117
|
/**
|
|
@@ -93,7 +133,7 @@ export declare function createScorePOIQuery(lookup: POIPhraseLookup, locale?: st
|
|
|
93
133
|
export declare function createScorePOICategory(lookup: POIPhraseLookup, locale?: string): (input: NormalizedInputLite, shape: QueryShapeLike) => number;
|
|
94
134
|
/**
|
|
95
135
|
* The whole-input category hit behind a `poi_category` verdict, for the marker's evidence. `null` when the input is not
|
|
96
|
-
* a bare category — same
|
|
136
|
+
* a bare category — same conditions as {@link createScorePOICategory}, so the two cannot disagree.
|
|
97
137
|
*/
|
|
98
138
|
export declare function matchPOICategory(text: string, locale: string | undefined, lookup: POIPhraseLookup): POIPhraseMatch | null;
|
|
99
139
|
//# sourceMappingURL=poi.d.ts.map
|
package/out/poi.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"poi.d.ts","sourceRoot":"","sources":["../poi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"poi.d.ts","sourceRoot":"","sources":["../lib/poi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAQjE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,OAAO,GAAG,mBAAmB,GAAG,MAAM,CAAA;IAClD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,MAAM,CAAA;IACpC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAChC;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,aAAa,CAAC,cAAc,CAAC,CAAA;AAEhG,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,QAAQ,GAAG,IAAI,CAAA;AAEjF,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;CACX;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B;;;OAGG;IACH,KAAK,EAAE,cAAc,CAAA;IACrB;;;;;;OAMG;IACH,OAAO,EAAE,cAAc,EAAE,CAAA;IACzB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,YAAY,CAAA;IACzB;;OAEG;IACH,QAAQ,CAAC,EAAE,kBAAkB,CAAA;IAC7B,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,YAAY,CAAA;CACzB;AAuCD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,MAAM,EAAE,eAAe,GACrB,eAAe,GAAG,IAAI,CAoExB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAClC,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,MAAM,GACb,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,KAAK,MAAM,CAiB/D;AAUD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACrC,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,MAAM,GACb,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,KAAK,MAAM,CAU/D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC/B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,MAAM,EAAE,eAAe,GACrB,cAAc,GAAG,IAAI,CAQvB"}
|
package/out/poi.js
CHANGED
|
@@ -36,11 +36,25 @@ const ANCHOR_SEPARATOR = /,\s*|\s(near|in|at|around|to)\s+/gi;
|
|
|
36
36
|
* Longest subject we accept, in tokens. Eight covers compound taxonomy phrases while bounding lexicon probes.
|
|
37
37
|
*/
|
|
38
38
|
const MAX_SUBJECT_TOKENS = 8;
|
|
39
|
+
/**
|
|
40
|
+
* The categories one candidate subject reaches, from the hits the lookup returned for it.
|
|
41
|
+
*
|
|
42
|
+
* The whole array when the first hit declares {@link POIPhraseMatch.searchAsSet} — carried as the lookup returned it,
|
|
43
|
+
* never filtered, so a rung that flagged only some of its members loses nothing here and the inconsistency stays
|
|
44
|
+
* visible to whoever reads the set. Otherwise the first hit alone, which is the preference-list reading the committed
|
|
45
|
+
* phrase index has always had.
|
|
46
|
+
*/
|
|
47
|
+
function reachedMatches(hits) {
|
|
48
|
+
return hits[0].searchAsSet ? [...hits] : [hits[0]];
|
|
49
|
+
}
|
|
39
50
|
/**
|
|
40
51
|
* Match a POI subject: the whole input, or the text before the FIRST anchor separator WHOSE PREFIX HITS THE LEXICON (≤
|
|
41
52
|
* 8 tokens). Scans separator occurrences left-to-right — a lexicon phrase may itself contain a bare separator word
|
|
42
53
|
* (e.g. "walk in clinic"), so the first separator isn't necessarily the right split point. Returns null when the
|
|
43
54
|
* lexicon never fires — including comma-ridden full addresses whose leading segment isn't a lexicon phrase.
|
|
55
|
+
*
|
|
56
|
+
* The winning candidate's hits are carried per {@link reachedMatches}: the first hit, or the whole set when the lookup
|
|
57
|
+
* declared one.
|
|
44
58
|
*/
|
|
45
59
|
export function matchPOISubject(text, locale, lookup) {
|
|
46
60
|
const trimmed = text.trim();
|
|
@@ -49,8 +63,10 @@ export function matchPOISubject(text, locale, lookup) {
|
|
|
49
63
|
return null;
|
|
50
64
|
const whole = lookup(trimmed, locale);
|
|
51
65
|
if (whole.length) {
|
|
66
|
+
const matches = reachedMatches(whole);
|
|
52
67
|
return {
|
|
53
|
-
match:
|
|
68
|
+
match: matches[0],
|
|
69
|
+
matches,
|
|
54
70
|
subject: trimmed,
|
|
55
71
|
subjectSpan: { text: trimmed, start: inputStart, end: inputStart + trimmed.length },
|
|
56
72
|
remainder: "",
|
|
@@ -60,7 +76,8 @@ export function matchPOISubject(text, locale, lookup) {
|
|
|
60
76
|
if (separator.index === 0)
|
|
61
77
|
continue;
|
|
62
78
|
const subject = trimmed.slice(0, separator.index).trim();
|
|
63
|
-
// Subjects only grow as the scan moves right — once over budget, later splits are too.
|
|
79
|
+
// Subjects only grow as the scan moves right — once over budget, later splits are too. Whitespace-only
|
|
80
|
+
// split, not `wordsOf`: a comma inside a subject is real content here, not a separator to erase.
|
|
64
81
|
if (subject.split(/\s+/).length > MAX_SUBJECT_TOKENS)
|
|
65
82
|
break;
|
|
66
83
|
const hits = lookup(subject, locale);
|
|
@@ -73,8 +90,10 @@ export function matchPOISubject(text, locale, lookup) {
|
|
|
73
90
|
const anchorOffset = trimmed.indexOf(remainder, separator.index + separator[0].length);
|
|
74
91
|
const relationText = separator[1] ?? ",";
|
|
75
92
|
const relationOffset = separator[1] ? separator.index + separator[0].indexOf(separator[1]) : separator.index;
|
|
93
|
+
const matches = reachedMatches(hits);
|
|
76
94
|
return {
|
|
77
|
-
match:
|
|
95
|
+
match: matches[0],
|
|
96
|
+
matches,
|
|
78
97
|
subject,
|
|
79
98
|
subjectSpan: {
|
|
80
99
|
text: subject,
|
|
@@ -147,7 +166,7 @@ export function createScorePOICategory(lookup, locale) {
|
|
|
147
166
|
}
|
|
148
167
|
/**
|
|
149
168
|
* The whole-input category hit behind a `poi_category` verdict, for the marker's evidence. `null` when the input is not
|
|
150
|
-
* a bare category — same
|
|
169
|
+
* a bare category — same conditions as {@link createScorePOICategory}, so the two cannot disagree.
|
|
151
170
|
*/
|
|
152
171
|
export function matchPOICategory(text, locale, lookup) {
|
|
153
172
|
const matched = matchPOISubject(text, locale, lookup);
|
package/out/poi.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"poi.js","sourceRoot":"","sources":["../poi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH;;;GAGG;AACH,MAAM,gBAAgB,GAAG,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"poi.js","sourceRoot":"","sources":["../lib/poi.ts"],"names":[],"mappings":"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"}
|
package/out/rules.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Bitter-lesson-safe: only universal structural patterns — no place-name dictionaries. ~1 small
|
|
10
10
|
* regex set per new locale, not 50K dictionary entries.
|
|
11
11
|
*/
|
|
12
|
-
import type { NormalizedInputLite, QueryShapeLike } from "
|
|
12
|
+
import type { NormalizedInputLite, QueryShapeLike } from "#types";
|
|
13
13
|
/**
|
|
14
14
|
* Longest input still plausible as a bare locality name, including a trailing region code.
|
|
15
15
|
*
|
|
@@ -33,10 +33,18 @@ export declare function scoreIntersection(input: NormalizedInputLite, _shape: Qu
|
|
|
33
33
|
*/
|
|
34
34
|
export declare function scoreLandmark(input: NormalizedInputLite, _shape: QueryShapeLike): number;
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* Split on whitespace + commas and drop empty words — the word grammar shared with `intent-rules.ts`. Dropping empties
|
|
37
|
+
* matters: a trailing comma or doubled separator otherwise yields an empty string that inflates the word count and
|
|
38
|
+
* falsifies every-word predicates like the proper-case check below.
|
|
38
39
|
*/
|
|
39
|
-
export declare
|
|
40
|
+
export declare function wordsOf(text: string): string[];
|
|
41
|
+
/**
|
|
42
|
+
* True when a word is USPS street-suffix vocabulary that unambiguously signals an address — the full Pub-28 table via
|
|
43
|
+
* `@mailwoman/codex`, minus its curated name-prone canonicals (PARK, FIELD, HILL, LAKE, …). Those double as ordinary
|
|
44
|
+
* proper-name heads ("Wrigley Field", "Menlo Park"), and disqualifying on them would reject the very venue and place
|
|
45
|
+
* names the rules below exist to capture. Shared with `intent-rules.ts` so both rule sets read one definition.
|
|
46
|
+
*/
|
|
47
|
+
export declare function isDisqualifyingStreetSuffix(word: string): boolean;
|
|
40
48
|
/**
|
|
41
49
|
* `landmark` rule (venue/named-place variant): short capitalized input with no street suffixes, no postcode hits, and
|
|
42
50
|
* no region abbreviations. Captures "Pier 39", "Empire State Building", "Wrigley Field", "Grand Central Terminal".
|
package/out/rules.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../lib/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAmBjE;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAA;AAkD1C;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAQrF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAQ5F;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,mBAAmB,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAQxF;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAE9C;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIjE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAkD5F;AAoBD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAc3F;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAe3F;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAmBhG;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,mBAAmB,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAEtF"}
|
package/out/rules.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* Bitter-lesson-safe: only universal structural patterns — no place-name dictionaries. ~1 small
|
|
10
10
|
* regex set per new locale, not 50K dictionary entries.
|
|
11
11
|
*/
|
|
12
|
+
import { NAME_PRONE_US_SUFFIXES, US_STREET_SUFFIX_LOOKUP } from "@mailwoman/codex/us/street-suffix";
|
|
12
13
|
/**
|
|
13
14
|
* Longest input still plausible as a bare venue or landmark name. Beyond it the query is carrying an address as well,
|
|
14
15
|
* and belongs to the structured-address scorer.
|
|
@@ -109,34 +110,23 @@ export function scoreLandmark(input, _shape) {
|
|
|
109
110
|
return 0;
|
|
110
111
|
}
|
|
111
112
|
/**
|
|
112
|
-
*
|
|
113
|
-
*
|
|
113
|
+
* Split on whitespace + commas and drop empty words — the word grammar shared with `intent-rules.ts`. Dropping empties
|
|
114
|
+
* matters: a trailing comma or doubled separator otherwise yields an empty string that inflates the word count and
|
|
115
|
+
* falsifies every-word predicates like the proper-case check below.
|
|
114
116
|
*/
|
|
115
|
-
export
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
"ct",
|
|
129
|
-
"court",
|
|
130
|
-
"pl",
|
|
131
|
-
"place",
|
|
132
|
-
"way",
|
|
133
|
-
"pkwy",
|
|
134
|
-
"parkway",
|
|
135
|
-
"hwy",
|
|
136
|
-
"highway",
|
|
137
|
-
"cir",
|
|
138
|
-
"circle",
|
|
139
|
-
]);
|
|
117
|
+
export function wordsOf(text) {
|
|
118
|
+
return text.split(/[\s,]+/).filter((word) => word.length > 0);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* True when a word is USPS street-suffix vocabulary that unambiguously signals an address — the full Pub-28 table via
|
|
122
|
+
* `@mailwoman/codex`, minus its curated name-prone canonicals (PARK, FIELD, HILL, LAKE, …). Those double as ordinary
|
|
123
|
+
* proper-name heads ("Wrigley Field", "Menlo Park"), and disqualifying on them would reject the very venue and place
|
|
124
|
+
* names the rules below exist to capture. Shared with `intent-rules.ts` so both rule sets read one definition.
|
|
125
|
+
*/
|
|
126
|
+
export function isDisqualifyingStreetSuffix(word) {
|
|
127
|
+
const canonical = US_STREET_SUFFIX_LOOKUP.get(word.trim().toLowerCase());
|
|
128
|
+
return canonical !== undefined && !NAME_PRONE_US_SUFFIXES.has(canonical);
|
|
129
|
+
}
|
|
140
130
|
/**
|
|
141
131
|
* `landmark` rule (venue/named-place variant): short capitalized input with no street suffixes, no postcode hits, and
|
|
142
132
|
* no region abbreviations. Captures "Pier 39", "Empire State Building", "Wrigley Field", "Grand Central Terminal".
|
|
@@ -159,10 +149,10 @@ export function scoreVenueLandmark(input, shape) {
|
|
|
159
149
|
const segCount = shape.segments?.length ?? 1;
|
|
160
150
|
if (segCount > 2)
|
|
161
151
|
return 0;
|
|
162
|
-
// Reject if any word is
|
|
163
|
-
const words = text
|
|
152
|
+
// Reject if any word is an unambiguous street suffix.
|
|
153
|
+
const words = wordsOf(text);
|
|
164
154
|
for (const w of words) {
|
|
165
|
-
if (
|
|
155
|
+
if (isDisqualifyingStreetSuffix(w))
|
|
166
156
|
return 0;
|
|
167
157
|
}
|
|
168
158
|
// Reject if the first token is a pure number (house-number-leading pattern).
|
package/out/rules.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../lib/rules.ts"],"names":[],"mappings":"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"}
|
package/out/types.d.ts
CHANGED
|
@@ -9,30 +9,8 @@
|
|
|
9
9
|
export type { LocaleHint, QueryIntentMarker, QueryKind, QueryKindResult } from "@mailwoman/core/pipeline";
|
|
10
10
|
export { QueryIntentCode } from "@mailwoman/core/pipeline";
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* The minimal input shapes consumed by `classifyKind` come from `@mailwoman/query-shape` — one declaration shared by
|
|
13
|
+
* every Stage-2.x consumer. `QueryShapeLike` stays this package's public name for its narrow read-only view.
|
|
13
14
|
*/
|
|
14
|
-
export
|
|
15
|
-
raw: string;
|
|
16
|
-
normalized: string;
|
|
17
|
-
appliedLocale?: string;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Minimal `QueryShape` shape consumed by `classifyKind`. Compatible with `@mailwoman/query-shape`'s output.
|
|
21
|
-
*/
|
|
22
|
-
export interface QueryShapeLike {
|
|
23
|
-
knownFormats: ReadonlyArray<{
|
|
24
|
-
format: string;
|
|
25
|
-
span: {
|
|
26
|
-
start: number;
|
|
27
|
-
end: number;
|
|
28
|
-
};
|
|
29
|
-
confidence: number;
|
|
30
|
-
}>;
|
|
31
|
-
segments?: ReadonlyArray<{
|
|
32
|
-
body: string;
|
|
33
|
-
index: number;
|
|
34
|
-
}>;
|
|
35
|
-
characterClass?: string;
|
|
36
|
-
totalLength?: number;
|
|
37
|
-
}
|
|
15
|
+
export type { NormalizedInputLite, QueryShapeSegmentsView as QueryShapeLike } from "@mailwoman/query-shape";
|
|
38
16
|
//# sourceMappingURL=types.d.ts.map
|
package/out/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AACzG,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAE1D
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../lib/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AACzG,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAE1D;;;GAGG;AACH,YAAY,EAAE,mBAAmB,EAAE,sBAAsB,IAAI,cAAc,EAAE,MAAM,wBAAwB,CAAA"}
|
package/out/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA"}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../lib/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mailwoman/kind-classifier",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.3.0",
|
|
4
4
|
"description": "Stage 2.5 of the runtime pipeline — categorize inputs by query shape (postcode_only / locality_only / structured_address / intersection / po_box / landmark / vague). Rule-based for v1.",
|
|
5
5
|
"license": "AGPL-3.0-only OR LicenseRef-Commercial",
|
|
6
6
|
"repository": {
|
|
@@ -24,30 +24,44 @@
|
|
|
24
24
|
"!test/**"
|
|
25
25
|
],
|
|
26
26
|
"type": "module",
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"imports": {
|
|
29
|
+
"#*": {
|
|
30
|
+
"types": "./out/*.d.ts",
|
|
31
|
+
"node": "./out/*.js",
|
|
32
|
+
"default": "./out/*.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
27
35
|
"exports": {
|
|
28
36
|
"./package.json": "./package.json",
|
|
29
37
|
".": {
|
|
30
38
|
"types": "./out/index.d.ts",
|
|
39
|
+
"node": "./out/index.js",
|
|
31
40
|
"default": "./out/index.js"
|
|
32
41
|
},
|
|
33
42
|
"./classify": {
|
|
34
43
|
"types": "./out/classify.d.ts",
|
|
44
|
+
"node": "./out/classify.js",
|
|
35
45
|
"default": "./out/classify.js"
|
|
36
46
|
},
|
|
37
47
|
"./types": {
|
|
38
48
|
"types": "./out/types.d.ts",
|
|
49
|
+
"node": "./out/types.js",
|
|
39
50
|
"default": "./out/types.js"
|
|
40
51
|
},
|
|
41
52
|
"./intent-rules": {
|
|
42
53
|
"types": "./out/intent-rules.d.ts",
|
|
54
|
+
"node": "./out/intent-rules.js",
|
|
43
55
|
"default": "./out/intent-rules.js"
|
|
44
56
|
},
|
|
45
57
|
"./rules": {
|
|
46
58
|
"types": "./out/rules.d.ts",
|
|
59
|
+
"node": "./out/rules.js",
|
|
47
60
|
"default": "./out/rules.js"
|
|
48
61
|
},
|
|
49
62
|
"./poi": {
|
|
50
63
|
"types": "./out/poi.d.ts",
|
|
64
|
+
"node": "./out/poi.js",
|
|
51
65
|
"default": "./out/poi.js"
|
|
52
66
|
}
|
|
53
67
|
},
|
|
@@ -57,34 +71,46 @@
|
|
|
57
71
|
"./package.json": "./package.json",
|
|
58
72
|
".": {
|
|
59
73
|
"types": "./out/index.d.ts",
|
|
74
|
+
"node": "./out/index.js",
|
|
60
75
|
"default": "./out/index.js"
|
|
61
76
|
},
|
|
62
77
|
"./classify": {
|
|
63
78
|
"types": "./out/classify.d.ts",
|
|
79
|
+
"node": "./out/classify.js",
|
|
64
80
|
"default": "./out/classify.js"
|
|
65
81
|
},
|
|
66
82
|
"./types": {
|
|
67
83
|
"types": "./out/types.d.ts",
|
|
84
|
+
"node": "./out/types.js",
|
|
68
85
|
"default": "./out/types.js"
|
|
69
86
|
},
|
|
70
87
|
"./intent-rules": {
|
|
71
88
|
"types": "./out/intent-rules.d.ts",
|
|
89
|
+
"node": "./out/intent-rules.js",
|
|
72
90
|
"default": "./out/intent-rules.js"
|
|
73
91
|
},
|
|
74
92
|
"./rules": {
|
|
75
93
|
"types": "./out/rules.d.ts",
|
|
94
|
+
"node": "./out/rules.js",
|
|
76
95
|
"default": "./out/rules.js"
|
|
77
96
|
},
|
|
78
97
|
"./poi": {
|
|
79
98
|
"types": "./out/poi.d.ts",
|
|
99
|
+
"node": "./out/poi.js",
|
|
80
100
|
"default": "./out/poi.js"
|
|
81
101
|
}
|
|
102
|
+
},
|
|
103
|
+
"imports": {
|
|
104
|
+
"#*": {
|
|
105
|
+
"types": "./out/*.d.ts",
|
|
106
|
+
"node": "./out/*.js",
|
|
107
|
+
"default": "./out/*.js"
|
|
108
|
+
}
|
|
82
109
|
}
|
|
83
110
|
},
|
|
84
111
|
"dependencies": {
|
|
85
|
-
"@mailwoman/
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
"@mailwoman/query-shape": "9.2.0"
|
|
112
|
+
"@mailwoman/codex": "9.3.0",
|
|
113
|
+
"@mailwoman/core": "9.3.0",
|
|
114
|
+
"@mailwoman/query-shape": "9.3.0"
|
|
89
115
|
}
|
|
90
116
|
}
|
package/types.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright Sister Software
|
|
3
|
-
* @license AGPL-3.0
|
|
4
|
-
* @author Teffen Ellis, et al.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Re-exports of the canonical types from `@mailwoman/core/pipeline`.
|
|
9
|
-
*/
|
|
10
|
-
export type { LocaleHint, QueryIntentMarker, QueryKind, QueryKindResult } from "@mailwoman/core/pipeline"
|
|
11
|
-
export { QueryIntentCode } from "@mailwoman/core/pipeline"
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Minimal `NormalizedInput` shape consumed by `classifyKind`. Compatible with `@mailwoman/normalize`'s output.
|
|
15
|
-
*/
|
|
16
|
-
export interface NormalizedInputLite {
|
|
17
|
-
raw: string
|
|
18
|
-
normalized: string
|
|
19
|
-
appliedLocale?: string
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Minimal `QueryShape` shape consumed by `classifyKind`. Compatible with `@mailwoman/query-shape`'s output.
|
|
24
|
-
*/
|
|
25
|
-
export interface QueryShapeLike {
|
|
26
|
-
knownFormats: ReadonlyArray<{
|
|
27
|
-
format: string
|
|
28
|
-
span: { start: number; end: number }
|
|
29
|
-
confidence: number
|
|
30
|
-
}>
|
|
31
|
-
segments?: ReadonlyArray<{ body: string; index: number }>
|
|
32
|
-
characterClass?: string
|
|
33
|
-
totalLength?: number
|
|
34
|
-
}
|