feat: better language selection
This commit is contained in:
@@ -21,21 +21,10 @@ func (p *DeepgramProvider) IsLocal() bool {
|
||||
}
|
||||
|
||||
func (p *DeepgramProvider) Models() []Model {
|
||||
// Nova-3 language support - maps to our 57 language list
|
||||
// from https://developers.deepgram.com/docs/models-languages-overview
|
||||
nova3Langs := []string{
|
||||
"ar", "be", "bs", "bg", "ca", "hr", "cs", "da", "nl", "en", "et", "fi",
|
||||
"fr", "de", "el", "hi", "hu", "id", "it", "ja", "kn", "ko", "lv", "lt",
|
||||
"mk", "ms", "mr", "no", "pl", "pt", "ro", "ru", "sr", "sk", "sl", "es",
|
||||
"sv", "tl", "ta", "tr", "uk", "vi",
|
||||
}
|
||||
|
||||
// Nova-2 language support - subset of nova-3
|
||||
nova2Langs := []string{
|
||||
"bg", "ca", "zh", "cs", "da", "nl", "en", "et", "fi", "fr", "de", "el",
|
||||
"hi", "hu", "id", "it", "ja", "ko", "lv", "lt", "ms", "no", "pl", "pt",
|
||||
"ro", "ru", "sk", "es", "sv", "th", "tr", "uk", "vi",
|
||||
}
|
||||
// https://developers.deepgram.com/docs/models-languages-overview
|
||||
nova3Langs := deepgramNova3Languages
|
||||
// https://developers.deepgram.com/docs/models-languages-overview
|
||||
nova2Langs := deepgramNova2Languages
|
||||
|
||||
docsURL := "https://developers.deepgram.com/docs/language"
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package provider
|
||||
|
||||
import "github.com/leonardotrapani/hyprvoice/internal/language"
|
||||
|
||||
// ElevenLabsProvider implements Provider for ElevenLabs services (transcription only)
|
||||
type ElevenLabsProvider struct{}
|
||||
|
||||
@@ -23,10 +21,9 @@ func (p *ElevenLabsProvider) IsLocal() bool {
|
||||
}
|
||||
|
||||
func (p *ElevenLabsProvider) Models() []Model {
|
||||
// ElevenLabs Scribe supports 90+ languages, including all 57 from our master list
|
||||
// See: https://elevenlabs.io/speech-to-text
|
||||
allLangs := language.AllLanguageCodes()
|
||||
docsURL := "https://elevenlabs.io/docs/capabilities/speech-to-text#supported-languages"
|
||||
// https://elevenlabs.io/speech-to-text
|
||||
allLangs := elevenLabsTranscriptionLanguages
|
||||
docsURL := "https://elevenlabs.io/speech-to-text"
|
||||
|
||||
return []Model{
|
||||
{
|
||||
|
||||
+30
-36
@@ -1,10 +1,6 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/leonardotrapani/hyprvoice/internal/language"
|
||||
)
|
||||
import "strings"
|
||||
|
||||
// GroqProvider implements Provider for Groq services
|
||||
type GroqProvider struct{}
|
||||
@@ -26,7 +22,8 @@ func (p *GroqProvider) IsLocal() bool {
|
||||
}
|
||||
|
||||
func (p *GroqProvider) Models() []Model {
|
||||
allLangs := language.AllLanguageCodes()
|
||||
// https://console.groq.com/docs/speech-to-text#supported-languages
|
||||
allLangs := groqTranscriptionLanguages
|
||||
docsURL := "https://console.groq.com/docs/speech-to-text#supported-languages"
|
||||
|
||||
return []Model{
|
||||
@@ -59,40 +56,37 @@ func (p *GroqProvider) Models() []Model {
|
||||
},
|
||||
// LLM models
|
||||
{
|
||||
ID: "llama-3.3-70b-versatile",
|
||||
Name: "Llama 3.3 70B Versatile",
|
||||
Description: "Most capable Llama model",
|
||||
Type: LLM,
|
||||
SupportsBatch: true,
|
||||
SupportsStreaming: false,
|
||||
Local: false,
|
||||
AdapterType: AdapterOpenAI,
|
||||
SupportedLanguages: allLangs,
|
||||
Endpoint: &EndpointConfig{BaseURL: "https://api.groq.com/openai", Path: "/v1/chat/completions"},
|
||||
ID: "llama-3.3-70b-versatile",
|
||||
Name: "Llama 3.3 70B Versatile",
|
||||
Description: "Most capable Llama model",
|
||||
Type: LLM,
|
||||
SupportsBatch: true,
|
||||
SupportsStreaming: false,
|
||||
Local: false,
|
||||
AdapterType: AdapterOpenAI,
|
||||
Endpoint: &EndpointConfig{BaseURL: "https://api.groq.com/openai", Path: "/v1/chat/completions"},
|
||||
},
|
||||
{
|
||||
ID: "llama-3.1-8b-instant",
|
||||
Name: "Llama 3.1 8B Instant",
|
||||
Description: "Fast and efficient",
|
||||
Type: LLM,
|
||||
SupportsBatch: true,
|
||||
SupportsStreaming: false,
|
||||
Local: false,
|
||||
AdapterType: AdapterOpenAI,
|
||||
SupportedLanguages: allLangs,
|
||||
Endpoint: &EndpointConfig{BaseURL: "https://api.groq.com/openai", Path: "/v1/chat/completions"},
|
||||
ID: "llama-3.1-8b-instant",
|
||||
Name: "Llama 3.1 8B Instant",
|
||||
Description: "Fast and efficient",
|
||||
Type: LLM,
|
||||
SupportsBatch: true,
|
||||
SupportsStreaming: false,
|
||||
Local: false,
|
||||
AdapterType: AdapterOpenAI,
|
||||
Endpoint: &EndpointConfig{BaseURL: "https://api.groq.com/openai", Path: "/v1/chat/completions"},
|
||||
},
|
||||
{
|
||||
ID: "mixtral-8x7b-32768",
|
||||
Name: "Mixtral 8x7B",
|
||||
Description: "Mixture of experts model",
|
||||
Type: LLM,
|
||||
SupportsBatch: true,
|
||||
SupportsStreaming: false,
|
||||
Local: false,
|
||||
AdapterType: AdapterOpenAI,
|
||||
SupportedLanguages: allLangs,
|
||||
Endpoint: &EndpointConfig{BaseURL: "https://api.groq.com/openai", Path: "/v1/chat/completions"},
|
||||
ID: "mixtral-8x7b-32768",
|
||||
Name: "Mixtral 8x7B",
|
||||
Description: "Mixture of experts model",
|
||||
Type: LLM,
|
||||
SupportsBatch: true,
|
||||
SupportsStreaming: false,
|
||||
Local: false,
|
||||
AdapterType: AdapterOpenAI,
|
||||
Endpoint: &EndpointConfig{BaseURL: "https://api.groq.com/openai", Path: "/v1/chat/completions"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package provider
|
||||
|
||||
var openaiTranscriptionLanguages = []string{
|
||||
"af", "ar", "hy", "az", "be", "bs", "bg", "ca", "zh", "hr", "cs", "da",
|
||||
"nl", "en", "et", "fi", "fr", "gl", "de", "el", "he", "hi", "hu", "is",
|
||||
"id", "it", "ja", "kn", "kk", "ko", "lv", "lt", "mk", "ms", "mr", "mi",
|
||||
"ne", "no", "fa", "pl", "pt", "ro", "ru", "sr", "sk", "sl", "es", "sw",
|
||||
"sv", "tl", "ta", "th", "tr", "uk", "ur", "vi", "cy",
|
||||
}
|
||||
|
||||
var groqTranscriptionLanguages = openaiTranscriptionLanguages
|
||||
var mistralTranscriptionLanguages = openaiTranscriptionLanguages
|
||||
var whisperTranscriptionLanguages = openaiTranscriptionLanguages
|
||||
|
||||
var whisperEnglishOnlyLanguages = []string{"en"}
|
||||
|
||||
var deepgramNova3Languages = []string{
|
||||
"multi",
|
||||
"ar", "ar-AE", "ar-SA", "ar-QA", "ar-KW", "ar-SY", "ar-LB", "ar-PS", "ar-JO", "ar-EG", "ar-SD", "ar-TD", "ar-MA", "ar-DZ", "ar-TN", "ar-IQ", "ar-IR",
|
||||
"be", "bn", "bs", "bg", "ca", "hr", "cs", "da", "da-DK", "nl", "nl-BE",
|
||||
"en", "en-US", "en-AU", "en-GB", "en-IN", "en-NZ", "et", "fi", "fr", "fr-CA",
|
||||
"de", "de-CH", "el", "hi", "hu", "id", "it", "ja", "kn", "ko", "ko-KR",
|
||||
"lv", "lt", "mk", "ms", "mr", "no", "pl", "pt", "pt-BR", "pt-PT", "ro",
|
||||
"ru", "sr", "sk", "sl", "es", "es-419", "sv", "sv-SE", "tl", "ta", "te",
|
||||
"tr", "uk", "vi",
|
||||
}
|
||||
|
||||
var deepgramNova2Languages = []string{
|
||||
"multi",
|
||||
"bg", "ca", "zh", "zh-CN", "zh-Hans", "zh-TW", "zh-Hant", "zh-HK", "cs",
|
||||
"da", "da-DK", "nl", "nl-BE", "en", "en-US", "en-AU", "en-GB", "en-NZ", "en-IN",
|
||||
"et", "fi", "fr", "fr-CA", "de", "de-CH", "el", "hi", "hu", "id", "it", "ja",
|
||||
"ko", "ko-KR", "lv", "lt", "ms", "no", "pl", "pt", "pt-BR", "pt-PT", "ro",
|
||||
"ru", "sk", "es", "es-419", "sv", "sv-SE", "th", "th-TH", "tr", "uk", "vi",
|
||||
}
|
||||
|
||||
var elevenLabsTranscriptionLanguages = []string{
|
||||
"bel", "bos", "bul", "cat", "hrv", "ces", "dan", "nld", "eng", "est", "fin", "fra",
|
||||
"glg", "deu", "ell", "hun", "isl", "ind", "ita", "jpn", "kan", "lav", "mkd", "msa",
|
||||
"mal", "nor", "pol", "por", "ron", "rus", "slk", "spa", "swe", "tur", "ukr", "vie",
|
||||
"hye", "aze", "ben", "yue", "fil", "kat", "guj", "hin", "kaz", "lit", "mlt", "cmn",
|
||||
"mar", "nep", "ori", "fas", "srp", "slv", "swa", "tam", "tel",
|
||||
"afr", "ara", "asm", "ast", "mya", "hau", "heb", "jav", "kor", "kir", "ltz", "mri",
|
||||
"oci", "pan", "tgk", "tha", "uzb", "cym",
|
||||
"amh", "lug", "ibo", "gle", "khm", "kur", "lao", "mon", "nso", "pus", "sna", "snd",
|
||||
"som", "urd", "wol", "xho", "yor", "zul",
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package provider
|
||||
|
||||
import "github.com/leonardotrapani/hyprvoice/internal/language"
|
||||
|
||||
// MistralProvider implements Provider for Mistral services (transcription only)
|
||||
type MistralProvider struct{}
|
||||
|
||||
@@ -23,8 +21,9 @@ func (p *MistralProvider) IsLocal() bool {
|
||||
}
|
||||
|
||||
func (p *MistralProvider) Models() []Model {
|
||||
allLangs := language.AllLanguageCodes()
|
||||
docsURL := "https://docs.mistral.ai/capabilities/speech/"
|
||||
// https://docs.mistral.ai/capabilities/audio/
|
||||
allLangs := mistralTranscriptionLanguages
|
||||
docsURL := "https://docs.mistral.ai/capabilities/audio/"
|
||||
|
||||
return []Model{
|
||||
{
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package provider
|
||||
|
||||
import "github.com/leonardotrapani/hyprvoice/internal/language"
|
||||
|
||||
// ModelType represents the type of a model
|
||||
type ModelType int
|
||||
|
||||
@@ -22,7 +20,7 @@ type Model struct {
|
||||
AdapterType string // which adapter to use (e.g., "openai", "elevenlabs", "whisper-cpp")
|
||||
StreamingAdapter string // adapter for streaming mode (if different from AdapterType)
|
||||
StreamingEndpoint *EndpointConfig // endpoint for streaming mode (if different from Endpoint)
|
||||
SupportedLanguages []string // explicit list of supported language codes
|
||||
SupportedLanguages []string // explicit list of provider language codes
|
||||
Endpoint *EndpointConfig // nil for local models
|
||||
LocalInfo *LocalModelInfo // nil for cloud models
|
||||
DocsURL string // URL to provider's language support documentation
|
||||
@@ -69,9 +67,3 @@ func (m *Model) SupportsLanguage(code string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SupportsAllLanguages returns true if the model supports all 57 languages
|
||||
func (m *Model) SupportsAllLanguages() bool {
|
||||
allCodes := language.AllLanguageCodes()
|
||||
return len(m.SupportedLanguages) == len(allCodes)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/leonardotrapani/hyprvoice/internal/language"
|
||||
)
|
||||
import "testing"
|
||||
|
||||
func TestModel_NeedsDownload(t *testing.T) {
|
||||
tests := []struct {
|
||||
@@ -118,11 +114,9 @@ func TestModel_SupportsBothModes(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModel_SupportsLanguage(t *testing.T) {
|
||||
allCodes := language.AllLanguageCodes()
|
||||
|
||||
multilingualModel := Model{
|
||||
ID: "whisper-large-v3",
|
||||
SupportedLanguages: allCodes,
|
||||
SupportedLanguages: []string{"en", "es", "zh"},
|
||||
}
|
||||
|
||||
englishOnlyModel := Model{
|
||||
@@ -207,65 +201,6 @@ func TestModel_SupportsLanguage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestModel_SupportsAllLanguages(t *testing.T) {
|
||||
allCodes := language.AllLanguageCodes()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
model Model
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "model with all 57 languages",
|
||||
model: Model{
|
||||
ID: "whisper-large-v3",
|
||||
SupportedLanguages: allCodes,
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "english-only model",
|
||||
model: Model{
|
||||
ID: "base.en",
|
||||
SupportedLanguages: []string{"en"},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "model with some languages",
|
||||
model: Model{
|
||||
ID: "partial",
|
||||
SupportedLanguages: []string{"en", "es", "fr", "de"},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "model with empty languages",
|
||||
model: Model{
|
||||
ID: "empty",
|
||||
SupportedLanguages: []string{},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "model with nil languages",
|
||||
model: Model{
|
||||
ID: "nil",
|
||||
SupportedLanguages: nil,
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if got := tc.model.SupportsAllLanguages(); got != tc.expected {
|
||||
t.Errorf("SupportsAllLanguages() = %v, want %v", got, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestModelType_Constants(t *testing.T) {
|
||||
// verify ModelType constants exist and are distinct
|
||||
if Transcription == LLM {
|
||||
@@ -387,8 +322,8 @@ func TestAllTranscriptionModels_HaveDocsURL(t *testing.T) {
|
||||
expectedDocsURLs := map[string]string{
|
||||
"openai": "https://platform.openai.com/docs/guides/speech-to-text#supported-languages",
|
||||
"groq": "https://console.groq.com/docs/speech-to-text#supported-languages",
|
||||
"mistral": "https://docs.mistral.ai/capabilities/speech/",
|
||||
"elevenlabs": "https://elevenlabs.io/docs/capabilities/speech-to-text#supported-languages",
|
||||
"mistral": "https://docs.mistral.ai/capabilities/audio/",
|
||||
"elevenlabs": "https://elevenlabs.io/speech-to-text",
|
||||
"deepgram": "https://developers.deepgram.com/docs/language",
|
||||
"whisper-cpp": "https://github.com/openai/whisper#available-models-and-languages",
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ const (
|
||||
const (
|
||||
ConfigProviderOpenAI = "openai"
|
||||
ConfigProviderGroqTranscription = "groq-transcription"
|
||||
ConfigProviderGroqTranslation = "groq-translation"
|
||||
ConfigProviderMistralTranscription = "mistral-transcription"
|
||||
ConfigProviderElevenLabs = "elevenlabs"
|
||||
ConfigProviderDeepgram = "deepgram"
|
||||
@@ -44,7 +43,7 @@ const (
|
||||
// e.g. "groq-transcription" -> "groq", "mistral-transcription" -> "mistral"
|
||||
func BaseProviderName(configProvider string) string {
|
||||
switch configProvider {
|
||||
case ConfigProviderGroqTranscription, ConfigProviderGroqTranslation:
|
||||
case ConfigProviderGroqTranscription:
|
||||
return ProviderGroq
|
||||
case ConfigProviderMistralTranscription:
|
||||
return ProviderMistral
|
||||
|
||||
+21
-26
@@ -1,10 +1,6 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/leonardotrapani/hyprvoice/internal/language"
|
||||
)
|
||||
import "strings"
|
||||
|
||||
// OpenAIProvider implements Provider for OpenAI services
|
||||
type OpenAIProvider struct{}
|
||||
@@ -26,7 +22,8 @@ func (p *OpenAIProvider) IsLocal() bool {
|
||||
}
|
||||
|
||||
func (p *OpenAIProvider) Models() []Model {
|
||||
allLangs := language.AllLanguageCodes()
|
||||
// https://platform.openai.com/docs/guides/speech-to-text#supported-languages
|
||||
allLangs := openaiTranscriptionLanguages
|
||||
|
||||
docsURL := "https://platform.openai.com/docs/guides/speech-to-text#supported-languages"
|
||||
|
||||
@@ -77,28 +74,26 @@ func (p *OpenAIProvider) Models() []Model {
|
||||
},
|
||||
// LLM models
|
||||
{
|
||||
ID: "gpt-4o-mini",
|
||||
Name: "GPT-4o Mini",
|
||||
Description: "Fast and affordable GPT-4 variant",
|
||||
Type: LLM,
|
||||
SupportsBatch: true,
|
||||
SupportsStreaming: false,
|
||||
Local: false,
|
||||
AdapterType: AdapterOpenAI,
|
||||
SupportedLanguages: allLangs,
|
||||
Endpoint: &EndpointConfig{BaseURL: "https://api.openai.com", Path: "/v1/chat/completions"},
|
||||
ID: "gpt-4o-mini",
|
||||
Name: "GPT-4o Mini",
|
||||
Description: "Fast and affordable GPT-4 variant",
|
||||
Type: LLM,
|
||||
SupportsBatch: true,
|
||||
SupportsStreaming: false,
|
||||
Local: false,
|
||||
AdapterType: AdapterOpenAI,
|
||||
Endpoint: &EndpointConfig{BaseURL: "https://api.openai.com", Path: "/v1/chat/completions"},
|
||||
},
|
||||
{
|
||||
ID: "gpt-4o",
|
||||
Name: "GPT-4o",
|
||||
Description: "Most capable GPT-4 model",
|
||||
Type: LLM,
|
||||
SupportsBatch: true,
|
||||
SupportsStreaming: false,
|
||||
Local: false,
|
||||
AdapterType: AdapterOpenAI,
|
||||
SupportedLanguages: allLangs,
|
||||
Endpoint: &EndpointConfig{BaseURL: "https://api.openai.com", Path: "/v1/chat/completions"},
|
||||
ID: "gpt-4o",
|
||||
Name: "GPT-4o",
|
||||
Description: "Most capable GPT-4 model",
|
||||
Type: LLM,
|
||||
SupportsBatch: true,
|
||||
SupportsStreaming: false,
|
||||
Local: false,
|
||||
AdapterType: AdapterOpenAI,
|
||||
Endpoint: &EndpointConfig{BaseURL: "https://api.openai.com", Path: "/v1/chat/completions"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"github.com/leonardotrapani/hyprvoice/internal/language"
|
||||
"github.com/leonardotrapani/hyprvoice/internal/models/whisper"
|
||||
)
|
||||
import "github.com/leonardotrapani/hyprvoice/internal/models/whisper"
|
||||
|
||||
// WhisperCppProvider implements Provider for local whisper.cpp transcription
|
||||
type WhisperCppProvider struct{}
|
||||
@@ -25,8 +22,10 @@ func (p *WhisperCppProvider) IsLocal() bool {
|
||||
}
|
||||
|
||||
func (p *WhisperCppProvider) Models() []Model {
|
||||
allLangs := language.AllLanguageCodes()
|
||||
englishOnly := []string{"en"}
|
||||
// https://github.com/openai/whisper#available-models-and-languages
|
||||
allLangs := whisperTranscriptionLanguages
|
||||
// https://github.com/openai/whisper#available-models-and-languages
|
||||
englishOnly := whisperEnglishOnlyLanguages
|
||||
docsURL := "https://github.com/openai/whisper#available-models-and-languages"
|
||||
|
||||
whisperModels := whisper.ListModels()
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/leonardotrapani/hyprvoice/internal/language"
|
||||
)
|
||||
import "testing"
|
||||
|
||||
func TestWhisperCppProvider_GetProvider(t *testing.T) {
|
||||
p := GetProvider("whisper-cpp")
|
||||
@@ -88,20 +84,18 @@ func TestWhisperCppProvider_MultilingualModels(t *testing.T) {
|
||||
"large-v3": true,
|
||||
}
|
||||
|
||||
allLangs := language.AllLanguageCodes()
|
||||
|
||||
for _, m := range models {
|
||||
isMultilingual := multilingualIDs[m.ID]
|
||||
if isMultilingual {
|
||||
if len(m.SupportedLanguages) != len(allLangs) {
|
||||
t.Errorf("model %s: expected %d languages, got %d", m.ID, len(allLangs), len(m.SupportedLanguages))
|
||||
}
|
||||
if !m.SupportsAllLanguages() {
|
||||
t.Errorf("model %s: SupportsAllLanguages() should be true", m.ID)
|
||||
if len(m.SupportedLanguages) <= 1 {
|
||||
t.Errorf("model %s: expected multiple languages, got %d", m.ID, len(m.SupportedLanguages))
|
||||
}
|
||||
if !m.SupportsLanguage("es") {
|
||||
t.Errorf("model %s: SupportsLanguage('es') should be true", m.ID)
|
||||
}
|
||||
if !m.SupportsLanguage("en") {
|
||||
t.Errorf("model %s: SupportsLanguage('en') should be true", m.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user