"""Intent routing — pure regex, no AI, sub-millisecond. Loco-Soft Komm-Nr Format: Type-Buchstabe + 4-6 stellige Zahl, optional Space dazwischen. Type-Letter: N=Neu, T=Tageszul., V=Vorfuehr, D=Differenzbest., G=Gebraucht, L=Leihgabe. Beispiele: 'D9094', 'd9094', 'D 9094', 'n8093', 'L9083' Pure 4-6 stellige Zahlen werden als DVN-Lookup behandelt (DVN ist eindeutig). """ import re from dataclasses import dataclass from typing import Literal # Komm-Nr: Type + DVN (mit oder ohne Space) KOMM_NR_RE = re.compile(r"^[NTVDGLntvdgl]\s*\d{4,6}$") # DVN allein: pure Zahl 4-6 Stellen DVN_RE = re.compile(r"^\d{4,6}$") # VIN: 17 chars, no I/O/Q VIN_RE = re.compile(r"^[A-HJ-NPR-Z0-9]{17}$") @dataclass class Intent: type: Literal[ "komm_nr", "dvn", "vin", "autocomplete_only", "keyword_search", "empty", ] direct_redirect: bool = False normalized_query: str = "" def route(raw_query: str) -> Intent: q = (raw_query or "").strip() if not q: return Intent(type="empty", normalized_query="") # Rule 1: Komm-Nr (Type + DVN, e.g. 'D9094', 'D 9094', 'd9094') if KOMM_NR_RE.match(q): # Normalize: remove spaces, uppercase the type letter cleaned = q.replace(" ", "") normalized = cleaned[0].upper() + cleaned[1:] return Intent( type="komm_nr", direct_redirect=True, normalized_query=normalized, ) # Rule 2: Pure 4-6 digit number = DVN lookup (eindeutig) if DVN_RE.match(q): return Intent( type="dvn", direct_redirect=True, normalized_query=q, ) # Rule 3: 17-char alphanumeric (no IOQ) = VIN upper = q.upper().replace(" ", "") if VIN_RE.match(upper): return Intent( type="vin", direct_redirect=True, normalized_query=upper, ) # Rule 4: Single char only -> autocomplete (FTS unbrauchbar bei 1 Zeichen) if len(q) < 2: return Intent( type="autocomplete_only", normalized_query=q.lower(), ) # Rule 5: Default — keyword + fuzzy search return Intent( type="keyword_search", normalized_query=q.lower(), )