feat(pm): ignore sub-threshold voice transmissions (STT noise gate)

Whisper hallucinates short real words ("Test", "Thank you", "Okay") on
near-silent or noisy audio. Unfiltered these reached the backend as a wrong
readback attempt — counting toward the 3x-skip — and triggered paid
LLM-router calls. Drop voice (PTT) transcripts below NUXT_PUBLIC_PTT_MIN_WORDS
(default 2); text input is exempt so deliberate short commands still work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
leubeem
2026-06-29 11:35:47 +02:00
parent 1d09a9fc2f
commit 859da2fc0c
3 changed files with 24 additions and 0 deletions

View File

@@ -27,6 +27,12 @@ ROUTER_LLM_MODEL=gpt-5-mini
# disabled (the backend falls back to deterministic bad_next routing).
SERVICE_SECRET=CHANGE_ME
# PM radio training
# Minimum word count for a voice (PTT) transmission to be used; shorter
# transcripts are treated as STT noise/hallucination and ignored. Set to 1 to
# disable. See the "STT MINIMUM-WORD GATE" in app/pages/pm.vue.
NUXT_PUBLIC_PTT_MIN_WORDS=2
# ATC audio generation
ATC_OUT_DIR=./storage/atc
FLIGHTLAB_TTS_CACHE_DIR=./.cache/flightlab-tts

View File

@@ -3365,6 +3365,20 @@ const handlePilotTransmission = async (message: string, source: 'text' | 'ptt' =
// ("roger", "wilco") still contains letters/digits and passes.
if (!transcript || !/[a-z0-9]/i.test(transcript)) return
// STT MINIMUM-WORD GATE — search here if a spoken transmission was ignored.
// Voice (PTT) only: drop transcripts shorter than the configured minimum.
// Whisper hallucinates short real words ("Test", "Thank you", "Okay") on
// near-silent or noisy audio; left unfiltered those reach the backend as a
// (wrong) readback attempt — counting toward the 3x-skip — and now also
// trigger a paid LLM-router call. Text input is exempt so deliberate short
// commands still work. Threshold is the NUXT_PUBLIC_PTT_MIN_WORDS env var
// (default 2); set it to 1 to effectively disable the gate.
const minPttWords = Number(config.public.pttMinWords ?? 2)
if (source === 'ptt' && transcript.split(/\s+/).filter(Boolean).length < minPttWords) {
pmLog.info(`IGNORED short PTT transcript (<${minPttWords} words):`, transcript)
return
}
const prefix = source === 'ptt' ? 'Pilot (PTT)' : 'Pilot'
setLastTransmission(`${prefix}: ${transcript}`)

View File

@@ -58,6 +58,10 @@ export default defineNuxtConfig({
public: {
apiDocumentationUrl: '/api-docs',
radioBackendUrl: process.env.NUXT_PUBLIC_RADIO_BACKEND_URL || 'http://127.0.0.1:8000',
// Minimum word count for a voice (PTT) transmission to be used. Below
// this the transcript is treated as STT noise/hallucination and
// ignored. See the "STT MINIMUM-WORD GATE" in pages/pm.vue.
pttMinWords: Number(process.env.NUXT_PUBLIC_PTT_MIN_WORDS || 2),
},
},
vuetify: {