feat(canonical-lexicon): phrase-override katmanı — adjective+head yanlış-kovalar
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Skordan önce yüksek-güven phrase kuralları: "<sistem> yağı$" → Yağlar (end- anchor, "motor yağı filtresi/pompası" hariç), "(cam|silecek|kalorifer) motoru" → Elektrik, "zamanlama (dişli|zincir|kayış)" → Triger. ~1400 motor/şanzıman yanlış-kovası + ~230 silecek-motoru + ~790 zamanlama düzelir. 29/29 test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -225,6 +225,20 @@ describe("mapToCanonical", () => {
|
||||
expect(mapToCanonical("burkma cubugu").slug).toBe("suspansiyon");
|
||||
});
|
||||
|
||||
it("phrase overrides fix adjective+head mis-buckets", () => {
|
||||
// "<system> yağı" = fluid, not the system
|
||||
expect(mapToCanonical("motor yagi").slug).toBe("yaglar-sivilar");
|
||||
expect(mapToCanonical("sanziman yagi").slug).toBe("yaglar-sivilar");
|
||||
// but the PARTS keep their bucket (end-anchor excludes them)
|
||||
expect(mapToCanonical("motor yagi filtresi").slug).toBe("filtreler");
|
||||
expect(mapToCanonical("motor yagi pompasi").slug).toBe("motor");
|
||||
// body electric motors → elektrik, not engine
|
||||
expect(mapToCanonical("cam silecek motoru").slug).toBe("elektrik");
|
||||
expect(mapToCanonical("cam motoru").slug).toBe("elektrik");
|
||||
// timing gear → triger, not şanzıman
|
||||
expect(mapToCanonical("zamanlama dislileri").slug).toBe("triger-zincir-rulman");
|
||||
});
|
||||
|
||||
it("returns null when no keyword matches", () => {
|
||||
expect(mapToCanonical("romork duzenegi cikarilabilir").slug).toBeNull();
|
||||
// genuinely generic Mercedes names stay unmapped (better than wrong bucket)
|
||||
|
||||
@@ -317,7 +317,25 @@ export interface CanonicalMatch {
|
||||
* score; a runner-up within 1 point (no strong keyword decisively separated the
|
||||
* two) is flagged ambiguous. Confidence scales with the winning margin.
|
||||
*/
|
||||
// High-confidence PHRASE overrides, checked before the bag-of-words score.
|
||||
// They fix "adjective + head-noun" cases where a strong keyword wins on the
|
||||
// modifier instead of the head: "motor yağı" scores motor(3) but the head noun
|
||||
// is the oil. Order doesn't matter (first match wins); keep each tight.
|
||||
const OVERRIDES: [RegExp, string][] = [
|
||||
// "<system> yağı/yağları" (head = oil) → fluid. END-ANCHORED so the PARTS
|
||||
// "motor yağı filtresi / pompası / soğutucusu" are NOT captured (they keep
|
||||
// scoring to filtre / motor / soğutma).
|
||||
[/(motor|sanziman|diferansiyel|direksiyon|hidrolik|fren|torpido) yag(i|lari)$/, "yaglar-sivilar"],
|
||||
// body/electric motors (window/wiper/blower), not the engine.
|
||||
[/(cam|silecek|kalorifer|kaput) motor/, "elektrik"],
|
||||
// timing gear/chain/belt → triger, not şanzıman via the weak "dişli".
|
||||
[/zamanlama (disli|zincir|kayis|seti|gergi|kasnak)/, "triger-zincir-rulman"],
|
||||
];
|
||||
|
||||
export function mapToCanonical(folded: string): CanonicalMatch {
|
||||
for (const [re, slug] of OVERRIDES) {
|
||||
if (re.test(folded)) return { slug, ambiguous: false, confidence: 0.95 };
|
||||
}
|
||||
const toks = tokens(folded);
|
||||
let bestSlug: string | null = null;
|
||||
let best = 0;
|
||||
|
||||
Reference in New Issue
Block a user