Add Linux STT support + harden LLM normalization output - #13
Open
F0urO4 wants to merge 2 commits into
Open
Conversation
…recording) The STT path was macOS-only: listInputDevices() used system_profiler and startRecording() spawned sox with -t coreaudio. On Linux this meant /stt-mic returned 'No input devices found' and recording produced an empty WAV. - listInputDevices(): branch on process.platform; on Linux use 'pactl list short sources' and filter out .monitor sinks. Works with both PulseAudio and PipeWire (PipeWire exposes PulseAudio-compatible sources). - startRecording(): use -t pulseaudio on Linux instead of -t coreaudio. - Drop the sox 'silence 1 0.1 1%' effect on Linux: it gates recording start on input exceeding 1% and on PulseAudio often yields an empty WAV because the gate never triggers before the user speaks. Push-to- talk (ctrl+r toggle) does not need auto-start gating, so the effect is kept only on macOS where it was originally tuned. - README: document Linux install (apt/dnf + whisper.cpp build from source) and add a CPU performance note pointing CPU-only users at base.en instead of large-v3-turbo.
…quotes
Small local LLMs (e.g. llama3.2:3b via Ollama) often wrap their output
in preambles like 'Here is the cleaned-up transcription:' and surround
the text in quotes, despite the system prompt instructing output-only.
The user would then see the wrapper text appear in their OpenCode prompt
instead of just the cleaned transcription.
- Add cleanLLMOutput() to strip common preambles, surrounding quotes,
and trailing notes from LLM output. Exported and unit-tested.
- normalizeTranscription(): filter out auto-generated session titles
('New session - <timestamp>') before passing them to the LLM as
context. Small LLMs sometimes fold these literally into the output
('This is a new session: 2026-08-19T18:58:56.307Z') instead of using
them to resolve ambiguous references. Only meaningful titles are now
passed, and the prompt is clearer about not including them in output.
- Strengthen the user prompt: 'Output ONLY the cleaned text, no quotes,
no preamble'.
- Add tests for cleanLLMOutput covering preamble stripping, quote
removal, and pass-through of already-clean text.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The STT path was macOS-only.
listInputDevices()usedsystem_profilerandstartRecording()spawned sox with-t coreaudio. On Linux,/stt-micreturned "No input devices found" and recording produced an empty WAV. This PR adds Linux support and fixes two LLM normalization issues I hit while testing on Pop!_OS 24.04 (PipeWire).Changes
1. Linux STT support (
feat)listInputDevices(): branch onprocess.platform. On Linux, usepactl list short sourcesand filter out.monitorsinks. Works with both PulseAudio and PipeWire (PipeWire exposes PulseAudio-compatible sources).startRecording(): use-t pulseaudioon Linux instead of-t coreaudio.silence 1 0.1 1%effect on Linux: it gates recording start on input exceeding 1% and on PulseAudio often yields an empty WAV because the gate never triggers before the user speaks. Push-to-talk (ctrl+rtoggle) does not need auto-start gating, so the effect is kept only on macOS where it was originally tuned.apt/dnf+ whisper.cpp build from source) and add a CPU performance note pointing CPU-only users atbase.eninstead oflarge-v3-turbo(large-v3-turbotranscribes a 3s clip in ~36s on CPU vs ~2s forbase.en).2. LLM normalization output hardening (
fix)Small local LLMs (e.g.
llama3.2:3bvia Ollama) often wrap their output in preambles like"Here is the cleaned-up transcription:"and surround the text in quotes, despite the system prompt instructing output-only. The user would then see the wrapper text appear in their OpenCode prompt instead of just the cleaned transcription.cleanLLMOutput(): strips common preambles, surrounding quotes, and trailing notes from LLM output. Exported and unit-tested.normalizeTranscription(): filter out auto-generated session titles ("New session - <timestamp>") before passing them to the LLM as context. Small LLMs sometimes fold these literally into the output ("This is a new session: 2026-08-19T18:58:56.307Z") instead of using them to resolve ambiguous references. Only meaningful titles are now passed, and the prompt is clearer about not including them in output."Output ONLY the cleaned text, no quotes, no preamble".Testing
npm run check— 0 lint errors, formatting clean (oxlint + oxfmt)npm test— 12/12 tests pass (10 existing + 2 new forcleanLLMOutput)/stt-miccorrectly lists both built-in and USB microphonesctrl+r→ speak →ctrl+rrecords, transcribes withbase.en, normalizes via Ollama, and appends clean text to the OpenCode promptNotes
process.platform === "darwin"checks.silenceeffect change is the only behavioral difference on macOS: none — it's still applied on Darwin exactly as before.