stt

package
v0.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 27, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package stt provides speech-to-text transcription.

The hosted request is the one the Lisa project sends, which is where this method comes from (Task 20238): a mono upload to the OpenAI-compatible /audio/transcriptions endpoint on Groq, model whisper-large-v3-turbo, response_format verbose_json, temperature pinned to 0 so the same utterance transcribes the same way twice, plus an optional language hint and decoder prompt. "turbo" is the distilled decoder: Lisa picked it because someone dictating a sentence wants the answer in about the time the local `base` model takes just to load its weights.

Two entry points, deliberately

TranscribeHosted uploads and nothing else. TranscribeContext prefers the local openai-whisper CLI and only uploads when a key is configured and the local run failed.

The split is not a convenience. The hub must never cause a program to run on the control-plane host, so it calls TranscribeHosted, which cannot reach the CLI — tests/security proves that by walking the call graph. And the CLI runs local-first because the alternative is that audio starts leaving a developer's machine the day an unrelated GROQ_API_KEY appears in their environment. Neither default is about speed.

Index

Constants

View Source
const (

	// DefaultGroqModel is the model Lisa transcribes with. "turbo" is the
	// distilled decoder: same encoder as whisper-large-v3, a fraction of the
	// latency, and the accuracy difference does not show up on the one or two
	// spoken sentences a task title is made of.
	DefaultGroqModel = "whisper-large-v3-turbo"

	// DefaultEndpoint is Groq's OpenAI-compatible transcription route.
	DefaultEndpoint = "https://api.groq.com/openai/v1/audio/transcriptions"
)

Variables

This section is empty.

Functions

func HostedAvailable added in v0.0.2

func HostedAvailable(cfg Config) (bool, string)

HostedAvailable reports whether the hosted backend is configured, and says what is missing when it is not. It is the gate for callers that may not use the local CLI — see TranscribeHosted.

func Transcribe

func Transcribe(audioPath string, cfg Config) (string, error)

Transcribe reads audio from audioPath and returns the transcribed text.

Retained for callers that only want the string (cmd/listen.go). New code should prefer TranscribeContext, which is cancellable and reports which backend answered.

Types

type Config

type Config struct {
	// Provider: "whisper" (local CLI, the default) or "groq" (hosted). An
	// empty value means whisper — never "groq because a key exists", which
	// would make an unrelated environment variable start uploading audio.
	// Ignored entirely by TranscribeHosted, which is always hosted.
	Provider Provider

	// Model overrides the hosted model. Defaults to DefaultGroqModel.
	Model string

	// Endpoint overrides the hosted transcription URL, for an
	// OpenAI-compatible server that is not Groq.
	Endpoint string

	// GroqAPIKey authenticates the hosted backend. Falls back to the
	// GROQ_API_KEY environment variable.
	GroqAPIKey string

	// Language is an ISO-639-1 hint ("en", "de"). Empty lets Whisper detect
	// it, which is the right default for a hub whose users may not share one.
	Language string

	// Prompt biases the decoder — Whisper's prompt field, bounded by the
	// endpoint at 224 tokens. Used to spell project and tool names the model
	// would otherwise transcribe phonetically.
	Prompt string

	// WhisperModel selects the local CLI model: "base", "small", "medium",
	// "large". Defaults to "base".
	WhisperModel string

	// Timeout bounds one hosted request. Zero means defaultTimeout.
	Timeout time.Duration
}

Config holds speech-to-text configuration.

type Provider

type Provider string

Provider selects the STT backend.

const (
	ProviderWhisper Provider = "whisper"
	ProviderGroq    Provider = "groq"
)

type Result added in v0.0.2

type Result struct {
	Text     string  `json:"text"`
	Language string  `json:"language,omitempty"`
	Duration float64 `json:"duration,omitempty"`
	// Backend names which path produced this, so a caller can tell the user
	// whether they are paying Groq or burning local CPU.
	Backend Provider `json:"backend,omitempty"`
}

Result is one transcription.

func TranscribeContext added in v0.0.2

func TranscribeContext(ctx context.Context, audioPath string, cfg Config) (Result, error)

TranscribeContext transcribes audioPath, honouring ctx for cancellation.

audioPath must be a file on disk; the caller owns any temp cleanup.

func TranscribeHosted added in v0.0.2

func TranscribeHosted(ctx context.Context, audioPath string, cfg Config) (Result, error)

TranscribeHosted transcribes using the hosted endpoint only, never the local CLI. It is the entry point for anything serving an HTTP request.

The distinction is structural, not a flag, and that is the point. cloop's hub is built on "an HTTP request never causes a program to run on the control-plane host" — tests/security walks the call graph from every handler to (*exec.Cmd).Run and fails the build when one connects. A runtime switch would not satisfy that check and, more to the point, would not satisfy the guarantee: the local backend is a Python process, started per request, fed audio supplied by the caller, running unsandboxed beside the control plane.

So the two paths are two functions. The CLI, which runs on a developer's own machine where there is no such promise to keep, keeps the fallback through TranscribeContext; the hub cannot reach it from here.

func (Result) Silent added in v0.0.2

func (r Result) Silent() bool

Silent reports whether the transcript carries no actual words — an empty string, a lone ".", punctuation only.

It is a backstop, not the defence. Whisper never returns "nothing": handed two seconds of digital silence, this endpoint returns "Thank you." — a fluent stock phrase that this function cannot distinguish from a real short title, and neither can the response metadata. Measured against the live API, no_speech_prob comes back 0.0000 for silence and for speech alike, and avg_logprob differed by less (0.29 vs 0.21) than two real sentences differ from each other. There is no server-side discriminator to build on.

The real check is client-side, on the samples, before anything is uploaded — see listenForSound in assets/js/15-voice.js and assets/glasses.html. This catches the residue: a clip that got past the level gate and still produced nothing a human would call a title.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL