3525e6b8fb
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>
29 lines
1.0 KiB
Go
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
|
|
}
|