Skip to content

Commit 93b277f

Browse files
committed
_build_resolver: preserve raw rule body when sampling fails
1 parent ea754a8 commit 93b277f

2 files changed

Lines changed: 27 additions & 0 deletions

File tree

custom_components/closest_intent/conversation.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
VERSION,
4040
)
4141
from .matching import (
42+
_RULE_RE,
43+
_SLOT_RE,
4244
Candidate,
4345
Resolver,
4446
build_canonical,
@@ -70,6 +72,8 @@
7072
VERSION,
7173
)
7274
from matching import ( # type: ignore
75+
_RULE_RE,
76+
_SLOT_RE,
7377
Candidate,
7478
Resolver,
7579
build_canonical,
@@ -452,11 +456,22 @@ def _build_resolver(self, language: str, custom_docs: list[dict]) -> Resolver:
452456
intents = None
453457

454458
if intents is not None:
459+
raw_rules = (raw or {}).get("expansion_rules") or {}
455460
# Expansion rules -> list of surface forms.
456461
for name, rule in (intents.expansion_rules or {}).items():
457462
try:
458463
forms = list(_dedupe(sample_expression(rule.expression, intents)))
459464
except Exception:
465+
forms = []
466+
# When we can't expand a rule, do not leave its name in as a literal string.
467+
raw_text = raw_rules.get(name)
468+
if isinstance(raw_text, str) and raw_text:
469+
needs_raw = (
470+
not forms or _SLOT_RE.search(raw_text) or _RULE_RE.search(raw_text)
471+
)
472+
if needs_raw and raw_text not in forms:
473+
forms.insert(0, raw_text)
474+
if not forms:
460475
continue
461476
# Cap rule expansion to keep alternation explosions bounded.
462477
resolver.expansion_rules[name] = forms[: max(self._expansion_cap, 32)]

tests/test_matching.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,18 @@ def test_resolver_inlines_unknown_rule_as_wildcard_slot() -> None:
321321
assert r.inline_rules("<unknown> rest") == "{unknown} rest"
322322

323323

324+
def test_expand_pattern_rule_body_with_slot_produces_wildcard() -> None:
325+
"""When an <expansion rule>'s body is a {slot} reference, the rule must inline as the slot."""
326+
from const import SLOT_WILDCARD # type: ignore
327+
328+
r = Resolver(expansion_rules={"name": ["{name}"]})
329+
out = expand_pattern("<name> an", cap=16, resolver=r)
330+
texts = [t for (t, _, _) in out]
331+
slot_lists = [s for (_, _, s) in out]
332+
assert any(SLOT_WILDCARD in t for t in texts)
333+
assert ["name"] in slot_lists
334+
335+
324336
def test_expand_pattern_uses_resolver_rules() -> None:
325337
r = Resolver(expansion_rules={"gruss": ["hallo", "moin"]})
326338
out = expand_pattern("<gruss> closest_intent", cap=16, resolver=r)

0 commit comments

Comments
 (0)