Skip to content

Commit 268de71

Browse files
committed
treewide: diagnostics, debug tools, timing, logging cleanup
1 parent 4ca8623 commit 268de71

4 files changed

Lines changed: 254 additions & 44 deletions

File tree

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,13 +278,21 @@ The result is that usually, your commands will be handled by Hassil/`closest-int
278278

279279
Runs one sentence through the matcher and returns a structured response, including the matched intent, the candidate pattern that won, its score, the captured slots, the canonical sentence that (would have been) forwarded to Hassil.
280280
Optionally, you can actually forward it to Hassil to see what action this would trigger. (The action is not actually triggered though.)
281+
On a miss, also surfaces a sample of the `name`/`area`/`floor` slot lists so you can immediately see whether entity exposure is wired up.
282+
Set `debug_top_candidates: true` to additionally get the top 10 raw-scored candidates regardless of threshold to see why your intended candidate did not win.
281283

282284
Useful when a sentence unexpectedly doesn't match, slot capture is wrong, and so on.
283285

284286
#### `closest_intent.dump_candidates`
285287

286288
Debug-logs and returns the full per-language state. This includes every expanded candidate, every expansion rule and its surface forms, every slot list and its values.
287289

290+
Options:
291+
292+
- `include_builtins: true`: also build and include built-in intent candidates in the dump, even when the integration is configured without them. Best combined with `intent_filter` to keep the output manageable.
293+
- `intent_filter: <substring>`: case-insensitive substring filter on intent names (e.g. `HassTurnOn`).
294+
- `include_exposure: true`: adds a per-entity breakdown of which states are exposed to Assist. Useful when an entity isn't showing up in `slot_values['name']`, noisy otherwise.
295+
288296
Useful for "fuck why is this STILL not working".
289297

290298
#### Filing an issue

custom_components/closest_intent/__init__.py

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,23 @@ def _async_register_services(hass: HomeAssistant) -> None:
157157
if hass.services.has_service(DOMAIN, SERVICE_DUMP_CANDIDATES):
158158
return
159159

160+
dump_schema = vol.Schema(
161+
{
162+
vol.Optional("include_builtins", default=False): cv.boolean,
163+
vol.Optional("intent_filter"): cv.string,
164+
vol.Optional("include_exposure", default=False): cv.boolean,
165+
}
166+
)
167+
160168
async def _dump(call: ServiceCall) -> ServiceResponse:
169+
data = dump_schema(dict(call.data))
170+
include_builtins = data["include_builtins"]
171+
intent_filter = data.get("intent_filter")
172+
include_exposure = data["include_exposure"]
173+
161174
agents = hass.data.get(DOMAIN, {}).get(KEY_AGENT_INSTANCES, {})
162175
if not agents:
163-
_LOGGER.warning("closest_intent.dump_candidates: no agent instances registered yet")
176+
_LOGGER.warning("dump_candidates: no agent instances registered yet")
164177
return {
165178
"version": VERSION,
166179
"agents": {},
@@ -169,13 +182,29 @@ async def _dump(call: ServiceCall) -> ServiceResponse:
169182

170183
states: dict[str, dict] = {}
171184
for entry_id, agent in agents.items():
172-
state = agent.dump_state()
185+
builtin_overrides: dict | None = None
186+
if include_builtins:
187+
builtin_overrides = {}
188+
for lang, (resolver, _, _) in agent._pools.items():
189+
try:
190+
builtin_overrides[lang] = await agent._async_get_builtin_override(
191+
lang, resolver
192+
)
193+
except Exception: # pragma: no cover
194+
_LOGGER.exception(
195+
"dump_candidates[%s]: builtin override build failed",
196+
lang,
197+
)
198+
state = agent.dump_state(
199+
builtin_overrides=builtin_overrides,
200+
intent_filter=intent_filter,
201+
include_exposure=include_exposure,
202+
)
173203
states[entry_id] = state
174204
# Pretty-print at DEBUG so users can paste a single block when
175205
# filing issues. INFO line is a one-liner pointer.
176206
_LOGGER.info(
177-
"closest_intent.dump_candidates[%s]: %d candidate(s) across %d language(s); "
178-
"see DEBUG for details",
207+
"dump_candidates[%s]: %d candidate(s) across %d language(s) (full state at DEBUG)",
179208
entry_id,
180209
sum(
181210
lang_state["user_candidate_count"] + lang_state["builtin_candidate_count"]
@@ -187,11 +216,7 @@ async def _dump(call: ServiceCall) -> ServiceResponse:
187216
pretty = json.dumps(state, indent=2, ensure_ascii=False)
188217
except Exception: # pragma: no cover
189218
pretty = repr(state)
190-
_LOGGER.debug(
191-
"closest_intent.dump_candidates[%s] full state:\n%s",
192-
entry_id,
193-
pretty,
194-
)
219+
_LOGGER.debug("dump_candidates[%s] full state:\n%s", entry_id, pretty)
195220

196221
return {
197222
"version": VERSION,
@@ -213,6 +238,7 @@ async def _dump(call: ServiceCall) -> ServiceResponse:
213238
vol.Optional("entry_id"): cv.string,
214239
vol.Optional("include_builtins", default=False): cv.boolean,
215240
vol.Optional("run_official", default=False): cv.boolean,
241+
vol.Optional("debug_top_candidates", default=False): cv.boolean,
216242
}
217243
)
218244

@@ -239,16 +265,18 @@ async def _parse(call: ServiceCall) -> ServiceResponse:
239265
data["sentence"],
240266
run_official=data["run_official"],
241267
include_builtins=data["include_builtins"],
268+
debug_top_candidates=data["debug_top_candidates"],
242269
)
243270
result["entry_id"] = entry_id
244271
_LOGGER.info(
245-
"closest_intent.parse[%s][%s] %r -> matched=%s intent=%s canonical=%r",
272+
"parse[%s][%s] %r -> matched=%s intent=%s canonical=%r [%.1fms]",
246273
entry_id,
247274
language,
248275
data["sentence"],
249276
result.get("matched"),
250277
result.get("intent"),
251278
result.get("canonical"),
279+
(result.get("pools") or {}).get("match_ms", 0.0),
252280
)
253281
return result
254282

0 commit comments

Comments
 (0)