routing: default to transcribe, not the first preset

When no [routing] default is configured, default_preset fell back to
workflows[0]. If that was an LLM rewrite (e.g. "Improve text"), every
wakeword/voice command without a matching keyword was sent to the language
model — repeatedly failing with HTTP 502 when the LLM backend was down.

default_preset now prefers a transcribe-mode preset for the no-keyword
fallback, so the default action is plain transcription. Adds tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
mARTin-B78 2026-06-07 09:53:37 +02:00
parent 11e469b444
commit ae12eea861
3 changed files with 39 additions and 1 deletions

View File

@ -9,6 +9,13 @@ The version is defined in [`blitztext/__init__.py`](blitztext/__init__.py).
## [Unreleased] ## [Unreleased]
### Fixed
- **Voice-routing default went to a rewrite**: when no `[routing] default` preset
is set, the no-keyword fallback used the *first* preset — which, if that happened
to be an LLM rewrite (e.g. "Improve text"), sent every unrouted wakeword command
to the language model (and failed when the LLM was down). The fallback now
prefers a `transcribe` preset, so the default action is plain transcription.
## [1.3.0] - 2026-06-07 ## [1.3.0] - 2026-06-07
### Added ### Added

View File

@ -110,7 +110,15 @@ class Config:
@property @property
def default_preset(self) -> "Workflow | None": def default_preset(self) -> "Workflow | None":
return self.preset_by_name(self.routing_default) or (self.workflows[0] if self.workflows else None) named = self.preset_by_name(self.routing_default)
if named:
return named
if not self.workflows:
return None
# No explicit default configured: prefer a plain transcribe preset over
# whatever happens to be first, so the no-keyword fallback never silently
# routes to an LLM rewrite.
return next((w for w in self.workflows if w.mode == "transcribe"), self.workflows[0])
@property @property
def all_keywords(self) -> list[str]: def all_keywords(self) -> list[str]:

View File

@ -68,3 +68,26 @@ def test_properties():
] ]
assert cfg.preset_by_name("WF2").name == "WF2" assert cfg.preset_by_name("WF2").name == "WF2"
assert cfg.preset_by_name("NonExistent") is None assert cfg.preset_by_name("NonExistent") is None
def test_default_preset_prefers_transcribe_when_unset():
"""With no routing default, fall back to a transcribe preset, not whatever
happens to be first (which could be an LLM rewrite)."""
cfg = Config()
cfg.routing_default = ""
cfg.workflows = [
Workflow(name="Improve text", hotkey="", mode="rewrite"),
Workflow(name="Transcribe", hotkey="", mode="transcribe"),
]
dp = cfg.default_preset
assert dp is not None and dp.mode == "transcribe" and dp.name == "Transcribe"
def test_default_preset_honours_explicit_name():
cfg = Config()
cfg.workflows = [
Workflow(name="Improve text", hotkey="", mode="rewrite"),
Workflow(name="Transcribe", hotkey="", mode="transcribe"),
]
cfg.routing_default = "Improve text"
assert cfg.default_preset.name == "Improve text"