def finalize_dictation(
raw: str,
*,
polish: bool = True,
llm_polish: bool | None = None,
email_mode: bool | None = None,
use_dictionary: bool | None = None,
) -> dict[str, Any]:
"""Decide command vs paste; polish only on the paste path.
Command detection runs on raw STT first, then on locally filler-stripped
text (no dictionary / LLM) so « um open Spotify » still routes as a command.
"""
from diapason.speech.dictate_polish import polish_dictation, polish_pipeline
cfg_dict = True
cfg_llm = False
cfg_email = False
cfg_timeout = 2000
cfg_auto_learn = True
dict_path = ""
do_polish = bool(polish)
try:
from diapason.core.config import load_config
d = load_config().dictation
# Request polish=False always wins; polish=True also needs config on
do_polish = bool(polish) and bool(d.polish)
cfg_dict = (
bool(d.dictionary) if use_dictionary is None else bool(use_dictionary)
)
cfg_llm = bool(d.llm_polish) if llm_polish is None else bool(llm_polish)
mode = (d.email_mode or "off").strip().lower()
if email_mode is None:
if mode in ("on", "true", "1", "yes"):
cfg_email = True
elif mode == "auto":
try:
from diapason.desktop.frontmost import is_email_composer_context
cfg_email = is_email_composer_context()
except Exception:
cfg_email = False
else:
cfg_email = False
else:
cfg_email = bool(email_mode)
cfg_timeout = int(d.llm_timeout_ms or 2000)
dict_path = (d.dictionary_path or "").strip()
cfg_auto_learn = bool(getattr(d, "auto_learn", True))
except Exception:
do_polish = bool(polish)
if use_dictionary is not None:
cfg_dict = bool(use_dictionary)
if llm_polish is not None:
cfg_llm = bool(llm_polish)
if email_mode is not None:
cfg_email = bool(email_mode)
if not polish:
do_polish = False
text_gate = True
try:
from diapason.core.config import load_config as _lc
text_gate = bool(_lc().speech.wakeword.text_gate)
except Exception:
text_gate = True
from diapason.speech.wake_phrases import has_wake_word, strip_wake_word
wake_hit = bool(text_gate and has_wake_word(raw))
stripped = strip_wake_word(raw) if wake_hit else (raw or "").strip()
action = parse_voice_command(raw)
if action.kind == "none" and wake_hit and stripped:
action = parse_voice_command(stripped)
if action.kind == "none":
cleaned_for_cmd = polish_dictation(
stripped if wake_hit else raw, aggressive=True
)
if cleaned_for_cmd and cleaned_for_cmd != (raw or "").strip():
action = parse_voice_command(cleaned_for_cmd)
if action.kind != "none":
exec_result = execute_voice_action(action)
return {
"mode": "command",
"original": raw,
"text": stripped if wake_hit and stripped else raw,
"action": exec_result,
"meta": {"wake_word": wake_hit},
}
# Bare wake (« Hey Diapason ») → hint UI to open Talk
if wake_hit and not stripped:
return {
"mode": "wake",
"original": raw,
"text": "",
"action": None,
"meta": {"wake_word": True, "suggest": "talk_open"},
}
paste_source = stripped if wake_hit and stripped else raw
text = polish_pipeline(
paste_source,
polish=do_polish,
use_dictionary=cfg_dict and do_polish,
llm_polish=cfg_llm and do_polish,
email_mode=cfg_email,
llm_timeout_ms=cfg_timeout,
dictionary_path=dict_path or None,
)
learned = 0
if (
cfg_auto_learn
and do_polish
and cfg_dict
and (paste_source or "").strip()
and text.strip()
and text.strip() != (paste_source or "").strip()
):
try:
from diapason.speech.dictation_dictionary import learn_from_correction
entries = learn_from_correction(paste_source, text, path=dict_path or None)
learned = len(entries)
except Exception:
learned = 0
return {
"mode": "paste",
"original": raw,
"text": text,
"action": None,
"meta": {
"dictionary": cfg_dict and do_polish,
"llm_polish": cfg_llm and do_polish,
"email_mode": cfg_email,
"wake_word": wake_hit,
"auto_learn": cfg_auto_learn,
"learned": learned,
},
}