64 lines
2.6 KiB
Go
64 lines
2.6 KiB
Go
// Package voice implements asynchronous speech-to-text and text-to-speech for
|
|
// the Cyrene runtime, following DevDocs/design/06-多模态.md §1.
|
|
//
|
|
// Voice input (STT) is transcribed to text and fed into the same agent pipeline
|
|
// as a chat message; the reply is synthesized (TTS) back to audio. Both
|
|
// directions degrade through a provider chain — cloud → local → edge-tts/espeak
|
|
// → silent — so an unavailable provider falls through to the next rung instead
|
|
// of failing the whole turn. The stub default implementations are HTTP-based and
|
|
// testable with a stdlib httptest server; real cloud/local providers remain
|
|
// adaptation points for later milestones.
|
|
package voice
|
|
|
|
import "context"
|
|
|
|
// Audio is a piece of audio data plus its encoding.
|
|
type Audio struct {
|
|
// Format is the encoding, e.g. "wav", "mp3", "ogg".
|
|
Format string
|
|
// Data is the raw audio bytes.
|
|
Data []byte
|
|
}
|
|
|
|
// STT converts audio to text.
|
|
type STT interface {
|
|
// STT transcribes audio and returns the spoken text. ctx controls
|
|
// cancellation and must be honored.
|
|
STT(ctx context.Context, audio Audio) (string, error)
|
|
}
|
|
|
|
// TTS synthesizes speech audio from text.
|
|
type TTS interface {
|
|
// TTS synthesizes audio from text. ctx controls cancellation and must be
|
|
// honored.
|
|
TTS(ctx context.Context, text string) (Audio, error)
|
|
}
|
|
|
|
// VoiceCloner creates a custom voice (声音复刻) from a public reference audio URL.
|
|
// A nil-cloner provider (no cloud rung configured) returns ErrNotConfigured.
|
|
type VoiceCloner interface {
|
|
// CloneVoice creates a custom voice and returns its voice_id. referenceURL is
|
|
// a public audio URL; prefix is the voice-name prefix (≤10 alphanumeric chars).
|
|
CloneVoice(ctx context.Context, referenceURL, prefix string) (string, error)
|
|
}
|
|
|
|
// ctxKeyTTSInstruction is the context key for a per-request TTS instruction.
|
|
// The TTS interface only carries (ctx, text), so an optional instruction is
|
|
// threaded through ctx to reach the adapter without widening the interface.
|
|
type ctxKeyTTSInstruction struct{}
|
|
|
|
// WithTTSInstruction returns ctx carrying an optional per-request TTS instruction
|
|
// (emotion/tone) that overrides the configured VOICE_TTS_INSTRUCTION for one call.
|
|
func WithTTSInstruction(ctx context.Context, instruction string) context.Context {
|
|
return context.WithValue(ctx, ctxKeyTTSInstruction{}, instruction)
|
|
}
|
|
|
|
// TTSInstructionFromContext returns the per-request TTS instruction set by
|
|
// WithTTSInstruction, or "" when none is carried.
|
|
func TTSInstructionFromContext(ctx context.Context) string {
|
|
if v, ok := ctx.Value(ctxKeyTTSInstruction{}).(string); ok {
|
|
return v
|
|
}
|
|
return ""
|
|
}
|