Integrating asr/tts models w/ new security config
This commit is contained in:
parent
1e8cabb063
commit
33b7729252
6 changed files with 38 additions and 23 deletions
|
|
@ -46,20 +46,23 @@ func supportsAudioTranscription(model string) bool {
|
||||||
func DetectTranscriber(cfg *config.Config) Transcriber {
|
func DetectTranscriber(cfg *config.Config) Transcriber {
|
||||||
if modelName := strings.TrimSpace(cfg.Voice.ModelName); modelName != "" {
|
if modelName := strings.TrimSpace(cfg.Voice.ModelName); modelName != "" {
|
||||||
modelCfg, err := cfg.GetModelConfig(modelName)
|
modelCfg, err := cfg.GetModelConfig(modelName)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return nil
|
protocol, _ := providers.ExtractProtocol(modelCfg.Model)
|
||||||
}
|
if protocol == "elevenlabs" && modelCfg.APIKey() != "" {
|
||||||
if supportsAudioTranscription(modelCfg.Model) {
|
return NewElevenLabsTranscriber(modelCfg.APIKey(), modelCfg.APIBase)
|
||||||
return NewAudioModelTranscriber(modelCfg)
|
}
|
||||||
|
if supportsAudioTranscription(modelCfg.Model) {
|
||||||
|
return NewAudioModelTranscriber(modelCfg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ElevenLabs voice config (supports Scribe STT).
|
// Fall back to scanning ModelList for suitable ASR providers
|
||||||
if key := strings.TrimSpace(cfg.Voice.ElevenLabsAPIKey); key != "" {
|
|
||||||
return NewElevenLabsTranscriber(key)
|
|
||||||
}
|
|
||||||
// Fall back to any model-list entry that uses the groq/ protocol.
|
|
||||||
for _, mc := range cfg.ModelList {
|
for _, mc := range cfg.ModelList {
|
||||||
|
protocol, _ := providers.ExtractProtocol(mc.Model)
|
||||||
|
if protocol == "elevenlabs" && mc.APIKey() != "" {
|
||||||
|
return NewElevenLabsTranscriber(mc.APIKey(), mc.APIBase)
|
||||||
|
}
|
||||||
if (strings.HasPrefix(mc.Model, "groq/") || mc.ModelName == "groq" || mc.Model == "whisper-large-v3-turbo") &&
|
if (strings.HasPrefix(mc.Model, "groq/") || mc.ModelName == "groq" || mc.Model == "whisper-large-v3-turbo") &&
|
||||||
mc.APIKey() != "" {
|
mc.APIKey() != "" {
|
||||||
return NewGroqTranscriber(mc.APIKey())
|
return NewGroqTranscriber(mc.APIKey())
|
||||||
|
|
|
||||||
|
|
@ -127,15 +127,17 @@ func TestDetectTranscriber(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "elevenlabs voice config key",
|
name: "elevenlabs voice config key",
|
||||||
cfg: &config.Config{
|
cfg: &config.Config{
|
||||||
Voice: config.VoiceConfig{ElevenLabsAPIKey: "sk_elevenlabs_test"},
|
ModelList: []*config.ModelConfig{
|
||||||
|
{Model: "elevenlabs/scribe_v1", APIKeys: config.SimpleSecureStrings("sk_elevenlabs_test")},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
wantName: "elevenlabs",
|
wantName: "elevenlabs",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "elevenlabs takes priority over groq model list",
|
name: "elevenlabs takes priority over groq model list",
|
||||||
cfg: &config.Config{
|
cfg: &config.Config{
|
||||||
Voice: config.VoiceConfig{ElevenLabsAPIKey: "sk_elevenlabs_test"},
|
|
||||||
ModelList: []*config.ModelConfig{
|
ModelList: []*config.ModelConfig{
|
||||||
|
{Model: "elevenlabs/scribe_v1", APIKeys: config.SimpleSecureStrings("sk_elevenlabs_test")},
|
||||||
{
|
{
|
||||||
ModelName: "groq",
|
ModelName: "groq",
|
||||||
Model: "groq/llama-3.3-70b",
|
Model: "groq/llama-3.3-70b",
|
||||||
|
|
@ -149,10 +151,10 @@ func TestDetectTranscriber(t *testing.T) {
|
||||||
name: "voice model name takes priority over elevenlabs",
|
name: "voice model name takes priority over elevenlabs",
|
||||||
cfg: &config.Config{
|
cfg: &config.Config{
|
||||||
Voice: config.VoiceConfig{
|
Voice: config.VoiceConfig{
|
||||||
ModelName: "voice-gemini",
|
ModelName: "voice-gemini",
|
||||||
ElevenLabsAPIKey: "sk_elevenlabs_test",
|
|
||||||
},
|
},
|
||||||
ModelList: []*config.ModelConfig{
|
ModelList: []*config.ModelConfig{
|
||||||
|
{Model: "elevenlabs", APIKeys: config.SimpleSecureStrings("sk_elevenlabs_test")},
|
||||||
{
|
{
|
||||||
ModelName: "voice-gemini",
|
ModelName: "voice-gemini",
|
||||||
Model: "gemini/gemini-2.5-flash",
|
Model: "gemini/gemini-2.5-flash",
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,16 @@ type ElevenLabsTranscriber struct {
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewElevenLabsTranscriber(apiKey string) *ElevenLabsTranscriber {
|
func NewElevenLabsTranscriber(apiKey, apiBase string) *ElevenLabsTranscriber {
|
||||||
logger.DebugCF("voice", "Creating ElevenLabs transcriber", map[string]any{"has_api_key": apiKey != ""})
|
logger.DebugCF("voice", "Creating ElevenLabs transcriber", map[string]any{"has_api_key": apiKey != ""})
|
||||||
|
|
||||||
|
if apiBase == "" {
|
||||||
|
apiBase = "https://api.elevenlabs.io"
|
||||||
|
}
|
||||||
|
|
||||||
return &ElevenLabsTranscriber{
|
return &ElevenLabsTranscriber{
|
||||||
apiKey: apiKey,
|
apiKey: apiKey,
|
||||||
apiBase: "https://api.elevenlabs.io",
|
apiBase: apiBase,
|
||||||
httpClient: &http.Client{
|
httpClient: &http.Client{
|
||||||
Timeout: 120 * time.Second,
|
Timeout: 120 * time.Second,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import (
|
||||||
var _ Transcriber = (*ElevenLabsTranscriber)(nil)
|
var _ Transcriber = (*ElevenLabsTranscriber)(nil)
|
||||||
|
|
||||||
func TestElevenLabsTranscriberName(t *testing.T) {
|
func TestElevenLabsTranscriberName(t *testing.T) {
|
||||||
tr := NewElevenLabsTranscriber("sk_test")
|
tr := NewElevenLabsTranscriber("sk_test", "")
|
||||||
if got := tr.Name(); got != "elevenlabs" {
|
if got := tr.Name(); got != "elevenlabs" {
|
||||||
t.Errorf("Name() = %q, want %q", got, "elevenlabs")
|
t.Errorf("Name() = %q, want %q", got, "elevenlabs")
|
||||||
}
|
}
|
||||||
|
|
@ -43,7 +43,7 @@ func TestElevenLabsTranscribe(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
|
|
||||||
tr := NewElevenLabsTranscriber("sk_test")
|
tr := NewElevenLabsTranscriber("sk_test", "")
|
||||||
tr.apiBase = srv.URL
|
tr.apiBase = srv.URL
|
||||||
|
|
||||||
resp, err := tr.Transcribe(context.Background(), audioPath)
|
resp, err := tr.Transcribe(context.Background(), audioPath)
|
||||||
|
|
@ -64,7 +64,7 @@ func TestElevenLabsTranscribe(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
|
|
||||||
tr := NewElevenLabsTranscriber("sk_bad")
|
tr := NewElevenLabsTranscriber("sk_bad", "")
|
||||||
tr.apiBase = srv.URL
|
tr.apiBase = srv.URL
|
||||||
|
|
||||||
_, err := tr.Transcribe(context.Background(), audioPath)
|
_, err := tr.Transcribe(context.Background(), audioPath)
|
||||||
|
|
@ -74,7 +74,7 @@ func TestElevenLabsTranscribe(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("missing file", func(t *testing.T) {
|
t.Run("missing file", func(t *testing.T) {
|
||||||
tr := NewElevenLabsTranscriber("sk_test")
|
tr := NewElevenLabsTranscriber("sk_test", "")
|
||||||
_, err := tr.Transcribe(context.Background(), filepath.Join(tmpDir, "nonexistent.ogg"))
|
_, err := tr.Transcribe(context.Background(), filepath.Join(tmpDir, "nonexistent.ogg"))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected error for missing file, got nil")
|
t.Fatal("expected error for missing file, got nil")
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,12 @@ type TTSProvider interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
func DetectTTS(cfg *config.Config) TTSProvider {
|
func DetectTTS(cfg *config.Config) TTSProvider {
|
||||||
|
if modelName := strings.TrimSpace(cfg.Voice.TTSModelName); modelName != "" {
|
||||||
|
if mc, err := cfg.GetModelConfig(modelName); err == nil && mc.APIKey() != "" {
|
||||||
|
return NewOpenAITTSProvider(mc.APIKey(), mc.APIBase, mc.Proxy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, mc := range cfg.ModelList {
|
for _, mc := range cfg.ModelList {
|
||||||
if strings.Contains(strings.ToLower(mc.Model), "tts") && mc.APIKey() != "" {
|
if strings.Contains(strings.ToLower(mc.Model), "tts") && mc.APIKey() != "" {
|
||||||
return NewOpenAITTSProvider(mc.APIKey(), mc.APIBase, mc.Proxy)
|
return NewOpenAITTSProvider(mc.APIKey(), mc.APIBase, mc.Proxy)
|
||||||
|
|
|
||||||
|
|
@ -639,9 +639,9 @@ type DevicesConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type VoiceConfig struct {
|
type VoiceConfig struct {
|
||||||
ModelName string `json:"model_name,omitempty" env:"PICOCLAW_VOICE_MODEL_NAME"`
|
ModelName string `json:"model_name,omitempty" env:"PICOCLAW_VOICE_MODEL_NAME"`
|
||||||
EchoTranscription bool `json:"echo_transcription" env:"PICOCLAW_VOICE_ECHO_TRANSCRIPTION"`
|
TTSModelName string `json:"tts_model_name,omitempty" env:"PICOCLAW_VOICE_TTS_MODEL_NAME"`
|
||||||
ElevenLabsAPIKey string `json:"elevenlabs_api_key,omitempty" env:"PICOCLAW_VOICE_ELEVENLABS_API_KEY"`
|
EchoTranscription bool `json:"echo_transcription" env:"PICOCLAW_VOICE_ECHO_TRANSCRIPTION"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ModelConfig represents a model-centric provider configuration.
|
// ModelConfig represents a model-centric provider configuration.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue