dictation

package
v0.51.1 Latest Latest
Warning

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

Go to latest
Published: Jul 22, 2026 License: Apache-2.0 Imports: 28 Imported by: 0

Documentation

Overview

Package dictation implements the POST /v1/dictation/transcribe handler. It is a thin adapter around the Framework's STT router: accept audio bytes and metadata, normalize to canonical PCM via internal/server/audio, and delegate to the router. The handler has no knowledge of which provider serves the request; that's the router's job.

Wire protocol for the streaming Dictation WebSocket (GET /v1/dictation/stream/sessions/{id}/ws).

Control frames are JSON text messages with a required "type" field. Audio frames are binary messages containing raw PCM (default 16 kHz S16 mono, client → server only — the server never sends audio on this surface). The first frame a client sends must be a "start" message; after a "segment_done" the client may send another "start" to begin the next segment on the same socket (a keyboard keeps one warm connection across mic presses).

This file is deliberately free of build tags (mirroring internal/server/wyoming/wire.go) so the frame codecs are natively testable on any development host; the handler and adapter stay `//go:build linux`.

Index

Constants

View Source
const (
	StreamMsgStart    = "start"
	StreamMsgFinalize = "finalize"
	StreamMsgStop     = "stop"
	StreamMsgPing     = "ping"
)

Client-to-server message types.

View Source
const (
	StreamMsgReady       = "ready"
	StreamMsgTranscript  = "transcript"
	StreamMsgSegmentDone = "segment_done"
	StreamMsgError       = "error"
	StreamMsgSessionEnd  = "session_end"
	StreamMsgPong        = "pong"
)

Server-to-client message types.

View Source
const (
	StreamErrStartRequired        = "start_required"
	StreamErrSegmentActive        = "segment_active"
	StreamErrNoActiveSegment      = "no_active_segment"
	StreamErrStreamingUnavailable = "streaming_unavailable"
	StreamErrProviderStreamFailed = "provider_stream_failed"
	StreamErrProviderStreamClosed = "provider_stream_closed"
	StreamErrFormatUnsupported    = "format_unsupported"
	StreamErrInvalidFrame         = "invalid_frame"
)

Stable error codes emitted on StreamErrorFrame. Clients switch on these, never on the human-readable message.

View Source
const (
	StreamEndReasonClient      = "client"
	StreamEndReasonIdle        = "idle"
	StreamEndReasonMaxDuration = "max_duration"
	StreamEndReasonMaxAudio    = "max_audio"
	StreamEndReasonError       = "error"
	StreamEndReasonShutdown    = "shutdown"
)

Session end reasons carried on StreamSessionEndFrame.

Variables

This section is empty.

Functions

This section is empty.

Types

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler implements the dictation HTTP surface.

func New

func New(opts Options) (*Handler, error)

New constructs a Handler. The router must be non-nil; a zero maxBytes defaults to 25 MB to match the documented API contract.

func (*Handler) Mount

func (h *Handler) Mount(mux *http.ServeMux)

Mount registers the handler on the given mux at /v1/dictation/transcribe.

func (*Handler) ServeHTTP

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP routes the request by Content-Type.

type Options

type Options struct {
	Router                 Transcriber
	MaxUploadMB            int    // request body ceiling; 0 disables the limit (discouraged)
	MaxDecodedAudioSeconds int    // decoded PCM duration ceiling; 0 disables the decode-duration cap
	DefaultPrompt          string // applied when the request does not provide a prompt
	Store                  store.Store
	ActiveTemplateIDs      []string
	// DefaultProviderProfileID is the server's configured Dictation primary
	// (ModelSelection.Dictate.PrimaryProfileID). It is the lowest-precedence
	// provider preference: explicit request override → edge-injected user
	// preference → this default → the router's configured fallback order.
	DefaultProviderProfileID string
}

Options configures a single handler instance.

type StreamAdapter added in v0.51.1

type StreamAdapter struct {
	Session *wssession.ManagedSession
	Conn    *websocket.Conn
	Router  StreamRouter
	// IdleTimeout terminates a session without any client- or provider-side
	// activity. Zero disables the watchdog.
	IdleTimeout time.Duration
	// MaxDuration terminates a session after this wall-clock duration. Zero
	// disables the hard cap.
	MaxDuration time.Duration
	// MaxStreamAudio caps cumulative uploaded audio (decoded duration)
	// across all segments of this session. Zero disables the budget.
	MaxStreamAudio time.Duration
	// OnClose runs after the read pump has returned. Typically removes the
	// session from the manager.
	OnClose func()
	// contains filtered or unexported fields
}

StreamAdapter bridges one streaming-Dictation WebSocket to the STT router's provider-native dictation streams. One adapter handles exactly one session; a session carries any number of sequential segments (one provider stream per `start`), which is how a keyboard reuses a warm socket across mic presses.

func (*StreamAdapter) Run added in v0.51.1

func (a *StreamAdapter) Run(parent context.Context)

Run blocks until the session ends. The first frame from the client MUST be a `start` frame (enforced inside the read pump).

type StreamAudioFormat added in v0.51.1

type StreamAudioFormat struct {
	Encoding     string `json:"encoding,omitempty"`       // "linear16" (default) | "pcm16"
	SampleRateHz int    `json:"sample_rate_hz,omitempty"` // default 16000
	Channels     int    `json:"channels,omitempty"`       // default 1
}

StreamAudioFormat describes the binary PCM frames the client will send. Zero values normalize to SpeechKit's canonical 16 kHz S16 mono.

type StreamErrorFrame added in v0.51.1

type StreamErrorFrame struct {
	Type    string `json:"type"` // "error"
	Code    string `json:"code"`
	Message string `json:"message"`
}

StreamErrorFrame reports a recoverable protocol or provider error. Unless a session_end follows, the socket stays open and the client may retry with a new "start" (or fall back to POST /v1/dictation/transcribe).

type StreamHandler added in v0.51.1

type StreamHandler struct {
	// contains filtered or unexported fields
}

StreamHandler exposes the session-creation endpoint and the WS upgrade endpoint under /v1/dictation/stream/*.

func NewStreamHandler added in v0.51.1

func NewStreamHandler(opts StreamHandlerOptions) (*StreamHandler, error)

NewStreamHandler constructs the streaming handler.

func (*StreamHandler) Mount added in v0.51.1

func (h *StreamHandler) Mount(mux *http.ServeMux)

Mount wires the streaming endpoints onto mux:

POST   /v1/dictation/stream/sessions          — create session + mint ticket
DELETE /v1/dictation/stream/sessions/{id}     — force close a session
GET    /v1/dictation/stream/sessions/{id}/ws  — upgrade to WebSocket with Sec-WebSocket-Protocol: ticket.<ticket>

type StreamHandlerOptions added in v0.51.1

type StreamHandlerOptions struct {
	Manager *wssession.SessionManager
	Router  StreamRouter
	// PublicURL overrides request-derived scheme/host in returned ws_url
	// values (Docker/proxy deployments).
	PublicURL         string
	AllowedOrigins    []string
	TrustedProxyCIDRs []string
	// IdleTimeout terminates a session without any client- or provider-side
	// activity. Zero defaults to 5 minutes; negative disables.
	IdleTimeout time.Duration
	// MaxSessionDuration hard-caps a session's wall-clock lifetime. Zero
	// disables the cap.
	MaxSessionDuration time.Duration
	// MaxStreamAudio caps the cumulative PCM a session may upload, expressed
	// as decoded audio duration. Zero disables the budget.
	MaxStreamAudio time.Duration
	// ReadLimit caps per-frame bytes; zero or negative falls back to 64 KiB.
	ReadLimit int64
}

StreamHandlerOptions configures the streaming Dictation WebSocket surface.

type StreamPongFrame added in v0.51.1

type StreamPongFrame struct {
	Type string `json:"type"` // "pong"
}

StreamPongFrame answers a client ping.

type StreamReadyFrame added in v0.51.1

type StreamReadyFrame struct {
	Type      string `json:"type"` // "ready"
	SegmentID uint64 `json:"segment_id"`
}

StreamReadyFrame acknowledges a start frame: the provider stream is up and binary PCM may flow.

type StreamRouter added in v0.51.1

type StreamRouter interface {
	StartDictationStream(ctx context.Context, opts speechkit.DictationStreamOptions, format speaker.AudioFormat) (speechkit.DictationStream, error)
	HasDictationStreaming() bool
}

StreamRouter is the minimal surface the streaming handler needs from the STT router. The production implementation is `internal/router.Router`; tests provide a fake.

type StreamSegmentDoneFrame added in v0.51.1

type StreamSegmentDoneFrame struct {
	Type      string `json:"type"` // "segment_done"
	SegmentID uint64 `json:"segment_id"`
}

StreamSegmentDoneFrame closes a segment: the provider stream is flushed and no further transcript frames will arrive for this segment_id. The client may send the next "start".

type StreamSessionEndFrame added in v0.51.1

type StreamSessionEndFrame struct {
	Type   string `json:"type"` // "session_end"
	Reason string `json:"reason"`
}

StreamSessionEndFrame is terminal; the server closes the socket after sending it.

type StreamStartFrame added in v0.51.1

type StreamStartFrame struct {
	Type              string `json:"type"` // must be "start"
	Language          string `json:"language,omitempty"`
	Model             string `json:"model,omitempty"`
	ProviderProfileID string `json:"provider_profile_id,omitempty"`
	// InterimResults defaults to true when omitted — live partials are the
	// point of this endpoint. Send false explicitly for finals-only.
	InterimResults *bool              `json:"interim_results,omitempty"`
	EndpointingMs  int                `json:"endpointing_ms,omitempty"`
	TurnDetection  bool               `json:"turn_detection,omitempty"`
	Keyterms       []string           `json:"keyterms,omitempty"`
	PromptHint     string             `json:"prompt_hint,omitempty"`
	Diarization    bool               `json:"diarization,omitempty"`
	Format         *StreamAudioFormat `json:"format,omitempty"`
}

StreamStartFrame is the mandatory first client frame of every segment. It maps 1:1 onto the kernel's speechkit.DictationStreamOptions plus the PCM input format.

func (StreamStartFrame) AudioFormat added in v0.51.1

func (f StreamStartFrame) AudioFormat() speaker.AudioFormat

AudioFormat converts the start frame's format block into the kernel's speaker.AudioFormat, applying canonical defaults.

func (StreamStartFrame) Options added in v0.51.1

Options converts the start frame into kernel DictationStreamOptions.

func (StreamStartFrame) ValidFormat added in v0.51.1

func (f StreamStartFrame) ValidFormat() (ok bool, reason string)

ValidFormat reports whether the requested PCM format is one the adapter accepts, returning a stable reason string when it is not.

type StreamTranscriptFrame added in v0.51.1

type StreamTranscriptFrame struct {
	Type       string                     `json:"type"` // "transcript"
	SegmentID  uint64                     `json:"segment_id"`
	Sequence   int64                      `json:"sequence,omitempty"`
	Text       string                     `json:"text"`
	Done       bool                       `json:"done"`
	Language   string                     `json:"language,omitempty"`
	Confidence float64                    `json:"confidence,omitempty"`
	Provider   string                     `json:"provider,omitempty"`
	Model      string                     `json:"model,omitempty"`
	Words      []StreamWord               `json:"words,omitempty"`
	Speakers   *speaker.DiarizationResult `json:"speakers,omitempty"`
}

StreamTranscriptFrame carries a draft (done=false) or final (done=true) transcript for the active segment. Drafts may update UI state; only finals should be committed to the target text field.

type StreamWord added in v0.51.1

type StreamWord struct {
	Text       string  `json:"text"`
	Confidence float64 `json:"confidence,omitempty"`
	StartMs    int64   `json:"start_ms,omitempty"`
	EndMs      int64   `json:"end_ms,omitempty"`
}

StreamWord is the word-level confidence entry on transcript frames. It mirrors speechkit.WordConfidence with stable snake_case JSON names.

type Transcriber

type Transcriber interface {
	Route(ctx context.Context, audio []byte, audioDurationSecs float64, opts stt.TranscribeOpts) (*stt.Result, error)
}

Transcriber is the minimal surface the handler needs from an STT router. The production implementation is `internal/router.Router`; tests provide a fake.

Jump to

Keyboard shortcuts

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