mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-07-26 08:19:15 +00:00
Baseline groundwork for TTS expansion plan. Introduces a capabilities system that the follow-up plan (phase-01..04) builds on. Backend: - `audio.ParamSchema` / `ProviderCapabilities` types + per-provider capabilities.go for edge, elevenlabs, minimax, openai, gemini. - Gemini TTS provider (client, models, voices, wav encoder, multi-speaker via SpeakerVoice, audio-tag aware prompts). - TTSOptions gains `Params map[string]any` (read-only) + `Speakers`. - `VoiceListProvider` interface decouples HTTP voice handler from provider-specific impls. - `nested_keys.go` resolves dot-separated param paths for nested provider bodies (voice_settings.stability etc.). - Characterization + defaults-invariant tests per provider lock nil-params byte-equivalence before new params land. - `/v1/tts/capabilities` HTTP endpoint + integration coverage. - Dual-read tests (PG + SQLite) for tts_config. Frontend (web + desktop): - `DynamicParamForm` with depends-on evaluation, split into fields/logic modules. Slider primitive added. - `AudioTagPicker` + `MultiSpeakerEditor` for Gemini. - `voice-picker` refactored toward portal UX; combobox tightened. - tts-capabilities API client + typed hooks. - i18n catalogs (en/vi/zh) expanded; parity tests guard key drift. - TTS page reorganised (voice-model-section removed; playground + credentials + provider-setup split cleanly). Docs: codebase-summary, project-changelog, tts-provider-capabilities.
174 lines
6.2 KiB
Go
174 lines
6.2 KiB
Go
// Package audio unifies TTS, STT, Music, and SFX generation under a single
|
|
// Manager. Replaces internal/tts (surface preserved via internal/tts/alias.go
|
|
// backward-compat layer).
|
|
//
|
|
// Phase 1 delivers TTS parity; STT/Music/SFX interfaces ship as stubs —
|
|
// implementations land in Phase 3 (Music/SFX) and Phase 4 (STT).
|
|
package audio
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
)
|
|
|
|
// ErrStreamingNotSupported is returned when a caller requests streaming TTS
|
|
// on a provider that does not implement StreamingTTSProvider.
|
|
var ErrStreamingNotSupported = errors.New("streaming not supported")
|
|
|
|
// ---- TTS (implemented Phase 1) ----
|
|
|
|
// TTSProvider synthesizes text into audio bytes.
|
|
type TTSProvider interface {
|
|
Name() string
|
|
Synthesize(ctx context.Context, text string, opts TTSOptions) (*SynthResult, error)
|
|
}
|
|
|
|
// StreamingTTSProvider is the optional streaming extension of TTSProvider.
|
|
// Providers that support HTTP chunked streaming implement this to return
|
|
// bytes as they arrive, reducing time-to-first-byte for long texts.
|
|
type StreamingTTSProvider interface {
|
|
TTSProvider
|
|
SynthesizeStream(ctx context.Context, text string, opts TTSOptions) (*StreamResult, error)
|
|
}
|
|
|
|
// StreamResult is the output of a streaming TTS synthesis call. Callers MUST
|
|
// Close the Audio reader when finished to release the underlying HTTP body.
|
|
type StreamResult struct {
|
|
Audio io.ReadCloser // chunked audio stream — caller must Close
|
|
Extension string // file extension without dot: "mp3", "opus", "ogg"
|
|
MimeType string // e.g. "audio/mpeg", "audio/ogg"
|
|
}
|
|
|
|
// TTSOptions carries per-request synthesis parameters.
|
|
//
|
|
// Params is a read-only map view owned by the caller.
|
|
// Implementations MUST NOT mutate it. If a provider needs to merge
|
|
// defaults with caller values, it MUST copy before writing.
|
|
// READ-ONLY constraint is documented here and enforced by convention.
|
|
type TTSOptions struct {
|
|
Voice string // provider-specific voice ID
|
|
Model string // provider-specific model ID
|
|
Format string // output format: "mp3", "opus", etc.
|
|
Speakers []SpeakerVoice // multi-speaker TTS (Gemini); len>0 switches to multi mode
|
|
// Params is a READ-ONLY map owned by the caller. Provider implementations
|
|
// MUST NOT mutate it; copy before merging with defaults.
|
|
Params map[string]any // provider-specific knobs (nested keys like "voice_settings.stability" allowed)
|
|
}
|
|
|
|
// SpeakerVoice binds a speaker label to a voice ID for multi-speaker TTS.
|
|
// JSON tags match the frontend MultiSpeakerEditor shape so the persisted
|
|
// tts.gemini.speakers blob round-trips without transformation.
|
|
type SpeakerVoice struct {
|
|
Speaker string `json:"speaker"` // e.g. "host", "guest"
|
|
VoiceID string `json:"voiceId"` // provider-specific voice ID
|
|
}
|
|
|
|
// SynthResult is the output of a TTS synthesis call.
|
|
type SynthResult struct {
|
|
Audio []byte // raw audio bytes
|
|
Extension string // file extension without dot: "mp3", "opus", "ogg"
|
|
MimeType string // e.g. "audio/mpeg", "audio/ogg"
|
|
}
|
|
|
|
// AutoMode controls when TTS is automatically applied to replies.
|
|
type AutoMode string
|
|
|
|
const (
|
|
AutoOff AutoMode = "off" // Disabled
|
|
AutoAlways AutoMode = "always" // Apply to all eligible replies
|
|
AutoInbound AutoMode = "inbound" // Only if user sent audio/voice
|
|
AutoTagged AutoMode = "tagged" // Only if reply contains [[tts]] directive
|
|
)
|
|
|
|
// Mode controls which reply kinds get TTS.
|
|
type Mode string
|
|
|
|
const (
|
|
ModeFinal Mode = "final" // Only final replies (default)
|
|
ModeAll Mode = "all" // All replies including tool/block
|
|
)
|
|
|
|
// VoiceListProvider is implemented by TTS providers that support dynamic
|
|
// voice listing. The HTTP voices handler uses this interface so it is not
|
|
// coupled to a specific provider implementation.
|
|
type VoiceListProvider interface {
|
|
// ListVoices returns the provider's available voices.
|
|
// Implementations may cache results internally.
|
|
ListVoices(ctx context.Context) ([]Voice, error)
|
|
}
|
|
|
|
// ---- STT (Phase 4) ----
|
|
|
|
// STTProvider transcribes audio bytes to text.
|
|
type STTProvider interface {
|
|
Name() string
|
|
Transcribe(ctx context.Context, in STTInput, opts STTOptions) (*TranscriptResult, error)
|
|
}
|
|
|
|
// STTInput is the audio to transcribe. Bytes or FilePath may be set; FilePath
|
|
// preferred for large files to avoid memory pressure.
|
|
type STTInput struct {
|
|
Bytes []byte // raw audio bytes (in-memory)
|
|
FilePath string // path on disk (preferred for >1MB files)
|
|
MimeType string // e.g. "audio/ogg"
|
|
Filename string // original filename (used for multipart form)
|
|
}
|
|
|
|
// STTOptions tunes transcription.
|
|
type STTOptions struct {
|
|
Language string // BCP-47/ISO-639-1 hint, empty = auto-detect
|
|
ModelID string // provider-specific model ID (default "scribe_v1")
|
|
Diarize bool // enable speaker diarization
|
|
TimeoutMs int // per-call timeout override; 0 = provider default
|
|
}
|
|
|
|
// TranscriptResult is the output of transcription.
|
|
type TranscriptResult struct {
|
|
Text string // transcribed text
|
|
Language string // detected or hinted language
|
|
Duration float64 // audio duration in seconds (if returned by provider)
|
|
Provider string // provider name that produced the transcript
|
|
}
|
|
|
|
// ---- Music (stubs — implementations land in Phase 3) ----
|
|
|
|
// MusicProvider generates music from prompt + optional lyrics.
|
|
type MusicProvider interface {
|
|
Name() string
|
|
GenerateMusic(ctx context.Context, opts MusicOptions) (*AudioResult, error)
|
|
}
|
|
|
|
// MusicOptions controls music generation.
|
|
type MusicOptions struct {
|
|
Prompt string
|
|
Lyrics string
|
|
Instrumental bool
|
|
Duration int // seconds (ElevenLabs: converts to music_length_ms)
|
|
Model string // provider-specific model override
|
|
TimeoutSec int // 0 = provider default
|
|
}
|
|
|
|
// ---- SFX (implementations land in Phase 3) ----
|
|
|
|
// SFXProvider generates short sound effects from a prompt.
|
|
type SFXProvider interface {
|
|
Name() string
|
|
GenerateSFX(ctx context.Context, opts SFXOptions) (*AudioResult, error)
|
|
}
|
|
|
|
// SFXOptions controls SFX generation.
|
|
type SFXOptions struct {
|
|
Prompt string
|
|
Duration int // seconds (provider may cap)
|
|
}
|
|
|
|
// AudioResult is the shared output of music/SFX generation.
|
|
type AudioResult struct {
|
|
Audio []byte
|
|
Extension string
|
|
MimeType string
|
|
Model string // actual model used (optional, for observability)
|
|
Provider string // provider name that produced the audio (optional, for observability)
|
|
}
|