Files
sttlab-tech 3525e6b8fb initial commit: live meeting transcription pipeline
Go orchestrator: spawns audiotee for capture (system audio + mic, two
tracks), segments with an energy-threshold VAD, transcribes each segment
via qwen-asr (subprocess per segment, vendored as a pinned git submodule
in third_party/qwen-asr), and serves the live transcript over SSE while
also writing it durably to a text file. Includes a glossary/prompt-leakage
guard (internal/asr/leak.go) that discards a segment if the model echoes
the biasing prompt instead of transcribing.

cmd/transcriptor-ai is the main orchestrator; cmd/transcript-tail is a
minimal terminal SSE client. See CLAUDE.md for the full architecture and
the reasoning behind each choice (Go over Python/Swift/Rust, SSE over
WebSocket, why the VAD is a hand-rolled heuristic, etc).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-09 13:18:57 +02:00

29 lines
1.0 KiB
Go

package asr
import (
"bytes"
"context"
"os/exec"
"syscall"
)
// runFunc executes binary with args, feeding stdin, and returns
// stdout/stderr. It's a seam so tests can substitute a fake process instead
// of requiring the real qwen_asr binary and a multi-GB model on disk.
type runFunc func(ctx context.Context, binary string, args []string, stdin []byte) (stdout, stderr []byte, err error)
func runCommand(ctx context.Context, binary string, args []string, stdin []byte) ([]byte, []byte, error) {
cmd := exec.CommandContext(ctx, binary, args...)
cmd.Stdin = bytes.NewReader(stdin)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
// Run qwen_asr in its own process group so the terminal's Ctrl+C
// (SIGINT to the whole foreground process group) doesn't kill an
// in-flight transcription directly. It's still cancelable through ctx,
// same as before — this only stops the shell from also signaling it.
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
err := cmd.Run()
return stdout.Bytes(), stderr.Bytes(), err
}