aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AGENTS.md32
-rw-r--r--docs/superpowers/specs/2026-10-19-voice-input-design.md86
2 files changed, 118 insertions, 0 deletions
diff --git a/AGENTS.md b/AGENTS.md
index d4a2f2a..f0f433b 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -120,6 +120,36 @@ not import from it.
## Findings
+### Audio input, live check (2026-10-19)
+
+Gemma4-12B-qat-mtp hearing the clip itself (`input_audio`, base64 WAV,
+no `data:` prefix), no whisper. The 10 clips in
+`/data/voice-models/samples/stt/` (3 s each, `expected.tsv`) plus
+`it.wav`/`en.wav` (10 s). Spec:
+`docs/superpowers/specs/2026-10-19-voice-input-design.md`.
+
+- **Transcript only** ("Transcribe this audio exactly, in the language
+ spoken (Italian or English). Output only the transcript.", thinking
+ off, temperature 0.6): 10/10 right in meaning ("Ho nuove email." for
+ "Ho nuove mail?", "Ciao." for "Ciao"); both 10 s clips word for word.
+ ~0.55 s per 3 s clip, 0.9 s for 10 s, 1.3 s for the first (cold).
+ large-v3-turbo with beam 5 was 9/10 at 0.86 s.
+- Cost about 25 prompt tokens per second of audio: 110 for a 3 s clip
+ with the prompt, 285 for 10 s.
+- **Transcript and answer in one call**, with a prompt that named no
+ language: 2 of 10 transcripts came out Spanish ("¿Qué hora es?"), and
+ answers were in Spanish or English for Italian questions.
+- **Answering from the audio directly**, real system prompt, stamp and
+ 12 tools, first round only, 2 runs per clip: tool choice 13/14 (one
+ "What time is it?" answered from the stamp, right). But the 6 "Che ore
+ sono?" runs, answered without a tool, came back in Estonian ("Praegu on
+ kell 10:35.") 2 times, Spanish 3 times (once a non-answer, "Hay 24
+ horas en un día."), Italian once: 1/6 right. The same questions typed:
+ 20/20. ~70 more prompt tokens per clip. **Decision: transcribe first,
+ then ask the transcript as text.** History and search need the words
+ anyway.
+- Not tried: a clip long enough to cross the 2048 ubatch (~80 s).
+
### Attachments, live check (2026-10-16)
- An image reading "BLUE 42" (`magick`, 400x120) was answered `BLUE 42`,
@@ -732,6 +762,8 @@ kokoro-onnx GitHub release `model-files-v1.0`.
- **Local memory:** `docs/superpowers/plans/2026-09-30-local-memory.md`
still applies; its tasks 1-4 target this daemon, the voice-specific
bits of task 4 aside.
+- **Voice input:** `docs/superpowers/specs/2026-10-19-voice-input-design.md`,
+ the model transcribes, no whisper; the mic is the canvas's part.
- **llamachat ports**, if the canvas replaces llamachat for good:
attachments, skills, a model picker, model-written titles.
- **Phase 2 tools:** tools that write or modify (events, messages,
diff --git a/docs/superpowers/specs/2026-10-19-voice-input-design.md b/docs/superpowers/specs/2026-10-19-voice-input-design.md
new file mode 100644
index 0000000..860967a
--- /dev/null
+++ b/docs/superpowers/specs/2026-10-19-voice-input-design.md
@@ -0,0 +1,86 @@
+# Voice input: design
+
+Date: 2026-10-19. Status: proposed.
+
+## Goal
+
+Let the user speak a question to the canvas instead of typing it, with
+no STT server: the resident model hears the clip itself. Only when the
+loaded model reports `audio` in its `input_modalities`; whisper stays
+out of the text daemon. Replies stay text (no TTS here).
+
+## Verified before designing
+
+Gemma4-12B-qat-mtp, 10 short clips with known text
+(`/data/voice-models/samples/stt/`) plus two 10 s clips; AGENTS.md,
+Findings, "Audio input".
+
+- Asked for a transcript only, naming Italian or English: 10/10 right in
+ meaning, ~0.55 s per 3 s clip. large-v3-turbo was 9/10 at 0.86 s.
+- Asked to answer from the audio directly, with the real system prompt
+ and tools: tool choice 13/14, but "Che ore sono?" was answered in
+ Estonian or Spanish 5 times in 6. The same questions as text: 20/20.
+
+So the daemon transcribes first and then asks the transcript as text.
+
+## Protocol
+
+`ask` gains an optional `audio`: an absolute path to a WAV file. `text`
+must then be empty; `files` may come with it.
+
+```json
+{"type": "ask", "text": "", "audio": "/run/user/1000/assistant-mic.wav"}
+```
+
+As with `files`, the daemon reads the path itself and never deletes or
+moves it: the canvas owns the recording.
+
+`state` gains `transcribing`, sent before the transcript request. After
+it the reply runs as a typed `ask` with the transcript as `text`: the
+`user` event, history, search and the date stamp all see the words, not
+the audio.
+
+Errors answer with `error` (with `turn`) and nothing more is sent:
+
+- not an absolute path, missing, not a readable WAV (stdlib `wave`);
+- longer than 60 s: "recording too long". 60 s is about 1500 audio
+ tokens; past the 2048 ubatch an image crashed the model, and audio
+ has not been tried there;
+- a model whose `input_modalities` lacks `audio`: "<model> cannot hear
+ audio";
+- an empty transcript: "heard nothing".
+
+## Transcription
+
+One non-streamed-to-the-client request, thinking off, temperature 0.6,
+no tools, no history, no system prompt:
+
+- a text part, "Transcribe this audio exactly, in the language spoken
+ (Italian or English). Output only the transcript.";
+- an `input_audio` part, `{"data": <base64>, "format": "wav"}`, no
+ `data:` prefix (llamachat's finding).
+
+It goes through `ask()` with the reply's turn, so a stop while
+transcribing shuts the request like any other. The transcript is
+stripped and goes to `answer()` unchanged.
+
+## Testing
+
+`python3 assistant.py --test` gains, against the fake server:
+
+- an `audio` ask: the fake server receives the transcript request with
+ the `input_audio` part, then a second request whose user message is the
+ transcript text; the `user` event carries the transcript;
+- the "cannot hear audio" error with `/models` reporting `["text",
+ "image"]`;
+- each refusal: relative path, not a WAV, over 60 s, empty transcript,
+ `audio` with non-empty `text`.
+
+Live: the 10 clips through the socket, with the transcript and tool
+choice compared to the typed question.
+
+## Not included
+
+The microphone itself: recording, the button and the level meter belong
+to the canvas, specified in the quickshell repo. Spoken replies, a wake
+word, and keeping the audio in history.