Skip to content

Commit 7ecdfe6

Browse files
committed
v0.42.1: OGG Opus transcribe fix + config loader overlayFile fix
- transcribe tool: whisper.cpp cannot read OGG Opus audio. Added convertToWAV() that auto-detects unsupported formats and uses ffmpeg (16kHz mono WAV) before passing to whisper. Best-effort — falls through to original path if ffmpeg unavailable. - config loader: overlayFile() was missing Transcription field propagation. Adding transcription section to ~/.odek/config.json was silently ignored. Now properly propagates the pointer field. - docs: updated CHANGELOG (v0.42.0 + v0.42.1), TELEGRAM.md (auto-transcribe section with OGG→WAV docs), AGENTS.md (version)
1 parent 668513c commit 7ecdfe6

5 files changed

Lines changed: 91 additions & 4 deletions

File tree

AGENTS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ It provides context about the project's architecture, conventions, and how to up
1212
- **Binary:** `odek` — single static binary, ~12 MB, instant startup.
1313
- **Config:** Five-layer priority: `~/.odek/secrets.env``~/.odek/config.json``./odek.json``ODEK_*` env vars → CLI flags.
1414
- **Benchmark:** AIEB v2.0 — 80.3% (highest published agent score on the Autonomous Intelligence Engineering Benchmark).
15-
- **Version:** v0.41.1 — see latest tag at https://github.com/BackendStack21/odek/releases
15+
- **Version:** v0.42.1 — see latest tag at https://github.com/BackendStack21/odek/releases
1616

1717
## Source Layout
1818

@@ -87,7 +87,7 @@ ReAct cycle: observe → think → act → repeat.
8787
| Zero-fork data | `math_eval` — native arithmetic, `diff` — LCS diff, `json_query` — dot-path JSON, `tr` — text transform, `base64` — encode/decode |
8888
| File analysis | `glob` — fast glob find, `file_info` — stat metadata, `count_lines` — streaming line count, `word_count` — streaming word count, `checksum` — SHA256/SHA1/MD5, `sort` — sort lines, `head_tail` — first/last N lines |
8989
| Multi-pattern | `multi_grep` — N regex patterns parallel, `tree` — structured directory tree |
90-
| Audio | `transcribe` — local whisper.cpp audio transcription (OGG/WAV/MP3 → text) |
90+
| Audio | `transcribe` — local whisper.cpp audio transcription with auto-OGGWAV conversion via ffmpeg |
9191

9292
All gated by the `danger` security classifier with three actions: allow, deny, prompt.
9393
- `shell`: Classifies commands into risk classes (safe, local_write, system_write, destructive, network_egress, code_execution, install, blocked).

cmd/odek/transcribe_tool.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,36 @@ import (
1414
"github.com/BackendStack21/kode/internal/danger"
1515
)
1616

17+
// ── Audio Format Conversion ──────────────────────────────────────────────
18+
19+
// convertToWAV converts an audio file to WAV format using ffmpeg if needed.
20+
// Returns the path to the WAV file (may be the same as input if already WAV/MP3/FLAC
21+
// or if ffmpeg is unavailable/fails — in which case whisper will produce its own error).
22+
// The caller must remove the returned path if it differs from the input path.
23+
func convertToWAV(srcPath string) string {
24+
ext := strings.ToLower(filepath.Ext(srcPath))
25+
// whisper.cpp supports WAV, MP3, FLAC natively via dr_wav/dr_mp3/dr_flac.
26+
switch ext {
27+
case ".wav", ".mp3", ".flac":
28+
return srcPath
29+
}
30+
31+
// Check if ffmpeg is available
32+
if _, err := exec.LookPath("ffmpeg"); err != nil {
33+
return srcPath
34+
}
35+
36+
// Convert to WAV using ffmpeg — best-effort, fall through on failure.
37+
dstPath := srcPath + ".wav"
38+
cmd := exec.Command("ffmpeg", "-y", "-i", srcPath, "-acodec", "pcm_s16le", "-ar", "16000", "-ac", "1", dstPath)
39+
if err := cmd.Run(); err != nil {
40+
// If ffmpeg fails (corrupt file, unsupported codec, etc.),
41+
// just pass the original path — whisper will produce its own error.
42+
return srcPath
43+
}
44+
return dstPath
45+
}
46+
1747
// ── Resolved Paths ──────────────────────────────────────────────────
1848

1949
// whisperBinary attempts to locate the whisper CLI binary.
@@ -191,6 +221,15 @@ func (t *transcribeTool) Call(argsJSON string) (result string, err error) {
191221
}
192222
f.Close()
193223

224+
// Convert to WAV if needed (whisper.cpp doesn't support OGG Opus natively).
225+
wavPath := convertToWAV(args.Path)
226+
cleanup := func() {
227+
if wavPath != args.Path {
228+
os.Remove(wavPath)
229+
}
230+
}
231+
defer cleanup()
232+
194233
// Locate whisper binary
195234
binary, err := whisperBinary(t.transcriptionCfg)
196235
if err != nil {
@@ -216,7 +255,7 @@ func (t *transcribeTool) Call(argsJSON string) (result string, err error) {
216255
args2 := []string{
217256
"--model", modelPathResolved,
218257
"--output-json",
219-
"--file", args.Path,
258+
"--file", wavPath,
220259
}
221260
if lang != "" {
222261
args2 = append(args2, "--language", lang)

docs/CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,35 @@
11
# Changelog
22

3+
## v0.42.1 (2026-05-24) — OGG Opus Transcribe Fix
4+
5+
### Bug Fixes
6+
- **transcribe tool** — whisper.cpp cannot read OGG Opus audio (`dr_wav`/`dr_mp3` limitation). Telegram voice messages are OGG Opus → produced empty transcriptions silently. Added `convertToWAV()` that auto-detects unsupported formats and uses ffmpeg (16kHz mono WAV) before passing to whisper. Best-effort: falls through to original path if ffmpeg unavailable, so whisper's own error bubbles up
7+
- **config loader**`overlayFile()` was missing `Transcription` field propagation. Adding `"transcription"` to `~/.odek/config.json` was silently ignored. Now properly propagates the pointer field
8+
9+
### Stats
10+
- 43 insertions across 2 files (transcribe_tool.go, loader.go)
11+
- All 19 packages pass with `-race`
12+
13+
---
14+
15+
## v0.42.0 (2026-05-24) — Session Search
16+
17+
### New Tool: `session_search`
18+
- Built-in `session_search` tool — browse, search, and recall past sessions by keyword or browse most recent
19+
- Uses FTS5 full-text search on the sessions index JSON
20+
- Supports: keyword queries with OR/AND, phrase search, role filtering, prefix search
21+
- Returns LLM-summarized matching sessions with timestamps and previews
22+
- Zero new dependencies (stdlib `encoding/json` + FTS5 via sqlite)
23+
24+
### CI
25+
- Bumped `softprops/action-gh-release` from v2 to v4
26+
27+
### Stats
28+
- 212 insertions across 3 files
29+
- All tests pass with `-race`
30+
31+
---
32+
333
## v0.41.1 (2026-05-24) — Quality Hardening
434

535
### Bug Fixes

docs/TELEGRAM.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ The `Handler` struct routes incoming updates to the appropriate callback based o
167167
|---|---|---|
168168
| `OnTextMessage` | Plain text message | `(chatID int64, text string) (string, error)` |
169169
| `OnCommand` | Slash command (e.g. `/start`) | `(chatID int64, command, args string) (string, error)` |
170-
| `OnVoiceMessage` | Voice message (OGG) | `(chatID int64, fileID string) (string, error)` |
170+
| `OnVoiceMessage` | Voice message (OGG Opus) | `(chatID int64, messageID int, fileID string) (string, error)` |
171171
| `OnPhotoMessage` | Photo message | `(chatID int64, fileIDs []string) (string, error)` |
172172
| `OnCallbackQuery` | Inline keyboard callback | `(chatID int64, callbackData string) (string, error)` |
173173

@@ -292,6 +292,21 @@ Media files are saved to `~/.odek/media/` (created automatically on first downlo
292292
- Saves as `photo_<truncated_fileID>.<ext>` (default extension: `.jpg`)
293293
- Same fileID truncation as voice downloads
294294

295+
### Auto-Transcribe (Voice → Text)
296+
297+
When `transcription.auto_transcribe: true` is set in config and whisper is installed, voice messages are automatically transcribed into text before reaching the agent:
298+
299+
```
300+
Voice message received → DownloadVoice (OGG Opus to disk)
301+
→ convertToWAV (ffmpeg: OGG→16kHz mono WAV)
302+
→ whisper.cpp (local transcription)
303+
→ transcribed text injected as user message
304+
```
305+
306+
**OGG Opus handling:** whisper.cpp uses `dr_wav`/`dr_mp3` internally and does not support OGG Opus. The transcribe tool auto-detects unsupported formats and converts via ffmpeg. If ffmpeg is unavailable, the original file is passed to whisper which produces a clear error message.
307+
308+
**Fallback:** If auto-transcribe fails (ffmpeg unavailable, corrupt audio, whisper error), the agent receives the file path with a suggestion to use the `transcribe()` tool manually.
309+
295310
## Types (`types.go`)
296311

297312
The package defines Telegram API types used throughout:

internal/config/loader.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,9 @@ func overlayFile(base, override FileConfig) FileConfig {
905905
if override.InteractionMode != "" {
906906
base.InteractionMode = override.InteractionMode
907907
}
908+
if override.Transcription != nil {
909+
base.Transcription = override.Transcription
910+
}
908911
return base
909912
}
910913

0 commit comments

Comments
 (0)