feat: models test and fixes

This commit is contained in:
leonardotrapani
2026-02-02 01:13:33 +01:00
parent 2729882b9d
commit 195f9f5115
34 changed files with 507 additions and 154 deletions
+6 -3
View File
@@ -16,6 +16,10 @@ func (p *DeepgramProvider) ValidateAPIKey(key string) bool {
return len(key) > 0
}
func (p *DeepgramProvider) APIKeyURL() string {
return "https://console.deepgram.com/project/keys"
}
func (p *DeepgramProvider) IsLocal() bool {
return false
}
@@ -23,7 +27,6 @@ func (p *DeepgramProvider) IsLocal() bool {
func (p *DeepgramProvider) Models() []Model {
// 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"
@@ -32,7 +35,7 @@ func (p *DeepgramProvider) Models() []Model {
{
ID: "nova-3",
Name: "Nova-3",
Description: "Best accuracy, 40+ languages",
Description: "Best accuracy; streaming available for faster response",
Type: Transcription,
SupportsBatch: true,
SupportsStreaming: true,
@@ -46,7 +49,7 @@ func (p *DeepgramProvider) Models() []Model {
{
ID: "nova-2",
Name: "Nova-2",
Description: "Fast, 30+ languages, filler words",
Description: "Cheaper legacy model; still solid accuracy",
Type: Transcription,
SupportsBatch: true,
SupportsStreaming: true,
+1 -4
View File
@@ -29,11 +29,8 @@ func TestDeepgramProvider_Models(t *testing.T) {
t.Errorf("Models() returned %d models, want 2", len(models))
}
// all models should support both batch and streaming
// all models should support both streaming and batch
for _, m := range models {
if !m.SupportsBatch {
t.Errorf("model %s should support batch", m.ID)
}
if !m.SupportsStreaming {
t.Errorf("model %s should support streaming", m.ID)
}
+15 -5
View File
@@ -16,6 +16,10 @@ func (p *ElevenLabsProvider) ValidateAPIKey(key string) bool {
return len(key) > 0
}
func (p *ElevenLabsProvider) APIKeyURL() string {
return "https://elevenlabs.io/app/settings/api-keys"
}
func (p *ElevenLabsProvider) IsLocal() bool {
return false
}
@@ -29,40 +33,46 @@ func (p *ElevenLabsProvider) Models() []Model {
{
ID: "scribe_v1",
Name: "Scribe v1",
Description: "90+ languages, best accuracy",
Description: "Most accurate; best for precision-critical work",
Type: Transcription,
SupportsBatch: true,
SupportsStreaming: false,
Local: false,
AdapterType: AdapterElevenLabs,
StreamingAdapter: AdapterElevenLabsStream,
SupportedLanguages: allLangs,
Endpoint: &EndpointConfig{BaseURL: "https://api.elevenlabs.io", Path: "/v1/speech-to-text"},
StreamingEndpoint: &EndpointConfig{BaseURL: "wss://api.elevenlabs.io", Path: "/v1/speech-to-text/realtime"},
DocsURL: docsURL,
},
{
ID: "scribe_v2",
Name: "Scribe v2",
Description: "Lower latency batch transcription",
Description: "Faster processing with good accuracy",
Type: Transcription,
SupportsBatch: true,
SupportsStreaming: false,
Local: false,
AdapterType: AdapterElevenLabs,
StreamingAdapter: AdapterElevenLabsStream,
SupportedLanguages: allLangs,
Endpoint: &EndpointConfig{BaseURL: "https://api.elevenlabs.io", Path: "/v1/speech-to-text"},
StreamingEndpoint: &EndpointConfig{BaseURL: "wss://api.elevenlabs.io", Path: "/v1/speech-to-text/realtime"},
DocsURL: docsURL,
},
{
ID: "scribe_v2_realtime",
Name: "Scribe v2 Realtime",
Description: "Real-time streaming, <150ms latency",
Description: "Instant words as you speak; faster but costs more",
Type: Transcription,
SupportsBatch: false,
SupportsStreaming: true,
Local: false,
AdapterType: AdapterElevenLabsStream,
AdapterType: AdapterElevenLabs,
StreamingAdapter: AdapterElevenLabsStream,
SupportedLanguages: allLangs,
Endpoint: &EndpointConfig{BaseURL: "wss://api.elevenlabs.io", Path: "/v1/speech-to-text/realtime"},
Endpoint: &EndpointConfig{BaseURL: "https://api.elevenlabs.io", Path: "/v1/speech-to-text"},
StreamingEndpoint: &EndpointConfig{BaseURL: "wss://api.elevenlabs.io", Path: "/v1/speech-to-text/realtime"},
DocsURL: docsURL,
},
}
+8 -15
View File
@@ -17,6 +17,10 @@ func (p *GroqProvider) ValidateAPIKey(key string) bool {
return strings.HasPrefix(key, "gsk_")
}
func (p *GroqProvider) APIKeyURL() string {
return "https://console.groq.com/keys"
}
func (p *GroqProvider) IsLocal() bool {
return false
}
@@ -31,7 +35,7 @@ func (p *GroqProvider) Models() []Model {
{
ID: "whisper-large-v3",
Name: "Whisper Large v3",
Description: "Full Whisper v3 model, best accuracy",
Description: "Best accuracy; generous free tier makes this great default",
Type: Transcription,
SupportsBatch: true,
SupportsStreaming: false,
@@ -44,7 +48,7 @@ func (p *GroqProvider) Models() []Model {
{
ID: "whisper-large-v3-turbo",
Name: "Whisper Large v3 Turbo",
Description: "Faster Whisper v3 with slightly lower accuracy",
Description: "Faster with slight accuracy tradeoff; still very good",
Type: Transcription,
SupportsBatch: true,
SupportsStreaming: false,
@@ -58,7 +62,7 @@ func (p *GroqProvider) Models() []Model {
{
ID: "llama-3.3-70b-versatile",
Name: "Llama 3.3 70B Versatile",
Description: "Most capable Llama model",
Description: "Best quality cleanup; smart rewrites, free tier available",
Type: LLM,
SupportsBatch: true,
SupportsStreaming: false,
@@ -69,18 +73,7 @@ func (p *GroqProvider) Models() []Model {
{
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",
Description: "Very fast; good for simple cleanup tasks",
Type: LLM,
SupportsBatch: true,
SupportsStreaming: false,
+2
View File
@@ -34,6 +34,8 @@ var deepgramNova2Languages = []string{
"ru", "sk", "es", "es-419", "sv", "sv-SE", "th", "th-TH", "tr", "uk", "vi",
}
var deepgramFluxLanguages = []string{"en"}
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",
+6 -17
View File
@@ -16,6 +16,10 @@ func (p *MistralProvider) ValidateAPIKey(key string) bool {
return len(key) > 0
}
func (p *MistralProvider) APIKeyURL() string {
return "https://admin.mistral.ai/organization/api-keys"
}
func (p *MistralProvider) IsLocal() bool {
return false
}
@@ -29,27 +33,12 @@ func (p *MistralProvider) Models() []Model {
{
ID: "voxtral-mini-latest",
Name: "Voxtral Mini Latest",
Description: "Latest Voxtral model, best for most uses",
Description: "EU-hosted; good for data residency or Mistral ecosystem",
Type: Transcription,
SupportsBatch: true,
SupportsStreaming: true,
SupportsStreaming: false,
Local: false,
AdapterType: AdapterOpenAI,
StreamingAdapter: "mistral-streaming", // not yet implemented
SupportedLanguages: allLangs,
Endpoint: &EndpointConfig{BaseURL: "https://api.mistral.ai", Path: "/v1/audio/transcriptions"},
DocsURL: docsURL,
},
{
ID: "voxtral-mini-2507",
Name: "Voxtral Mini 2507",
Description: "Stable Voxtral version from July 2025",
Type: Transcription,
SupportsBatch: true,
SupportsStreaming: true,
Local: false,
AdapterType: AdapterOpenAI,
StreamingAdapter: "mistral-streaming", // not yet implemented
SupportedLanguages: allLangs,
Endpoint: &EndpointConfig{BaseURL: "https://api.mistral.ai", Path: "/v1/audio/transcriptions"},
DocsURL: docsURL,
+3 -3
View File
@@ -57,7 +57,7 @@ func TestModel_IsStreaming(t *testing.T) {
}{
{
name: "streaming-only model",
model: Model{ID: "scribe_v2_realtime", SupportsBatch: false, SupportsStreaming: true},
model: Model{ID: "flux-general-en", SupportsBatch: false, SupportsStreaming: true},
expected: true,
},
{
@@ -89,7 +89,7 @@ func TestModel_SupportsBothModes(t *testing.T) {
}{
{
name: "streaming-only model",
model: Model{ID: "scribe_v2_realtime", SupportsBatch: false, SupportsStreaming: true},
model: Model{ID: "flux-general-en", SupportsBatch: false, SupportsStreaming: true},
expected: false,
},
{
@@ -325,7 +325,7 @@ func TestAllTranscriptionModels_HaveDocsURL(t *testing.T) {
"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",
"whisper-cpp": "https://github.com/ggml-org/whisper.cpp#models",
}
for _, pName := range providers {
+10 -6
View File
@@ -17,6 +17,10 @@ func (p *OpenAIProvider) ValidateAPIKey(key string) bool {
return strings.HasPrefix(key, "sk-")
}
func (p *OpenAIProvider) APIKeyURL() string {
return "https://platform.openai.com/api-keys"
}
func (p *OpenAIProvider) IsLocal() bool {
return false
}
@@ -32,7 +36,7 @@ func (p *OpenAIProvider) Models() []Model {
{
ID: "whisper-1",
Name: "Whisper 1",
Description: "OpenAI's production speech-to-text model",
Description: "Reliable and cost-effective; good default for most use cases",
Type: Transcription,
SupportsBatch: true,
SupportsStreaming: false,
@@ -45,7 +49,7 @@ func (p *OpenAIProvider) Models() []Model {
{
ID: "gpt-4o-transcribe",
Name: "GPT-4o Transcribe",
Description: "High quality transcription with GPT-4o",
Description: "Top accuracy; slower and pricier but best quality",
Type: Transcription,
SupportsBatch: true,
SupportsStreaming: false,
@@ -58,7 +62,7 @@ func (p *OpenAIProvider) Models() []Model {
{
ID: "gpt-4o-mini-transcribe",
Name: "GPT-4o Mini Transcribe",
Description: "Fast transcription with GPT-4o Mini",
Description: "Good balance of speed, cost, and quality",
Type: Transcription,
SupportsBatch: true,
SupportsStreaming: false,
@@ -71,7 +75,7 @@ func (p *OpenAIProvider) Models() []Model {
{
ID: "gpt-4o-realtime-preview",
Name: "GPT-4o Realtime Preview",
Description: "Real-time streaming transcription with GPT-4o",
Description: "Instant words as you speak; fastest but most expensive",
Type: Transcription,
SupportsBatch: false,
SupportsStreaming: true,
@@ -85,7 +89,7 @@ func (p *OpenAIProvider) Models() []Model {
{
ID: "gpt-4o-mini",
Name: "GPT-4o Mini",
Description: "Fast and affordable GPT-4 variant",
Description: "Fast and cheap; good default for text cleanup",
Type: LLM,
SupportsBatch: true,
SupportsStreaming: false,
@@ -96,7 +100,7 @@ func (p *OpenAIProvider) Models() []Model {
{
ID: "gpt-4o",
Name: "GPT-4o",
Description: "Most capable GPT-4 model",
Description: "Best quality cleanup; pricier but smarter rewrites",
Type: LLM,
SupportsBatch: true,
SupportsStreaming: false,
+1
View File
@@ -11,6 +11,7 @@ type Provider interface {
Name() string
RequiresAPIKey() bool
ValidateAPIKey(key string) bool
APIKeyURL() string
IsLocal() bool
Models() []Model
DefaultModel(t ModelType) string
+13 -5
View File
@@ -279,7 +279,7 @@ func TestValidateModelLanguage_ErrorFormat(t *testing.T) {
}
// should contain docs URL
if !strings.Contains(errMsg, "https://github.com/openai/whisper") {
if !strings.Contains(errMsg, "https://github.com/ggml-org/whisper.cpp") {
t.Errorf("error should contain docs URL, got: %s", errMsg)
}
@@ -343,7 +343,7 @@ func TestElevenLabsProvider(t *testing.T) {
t.Errorf("ElevenLabsProvider.Models() = %d models, want 3", len(models))
}
// Check batch-only models
// Check batch + streaming models
scribeV1, err := GetModel("elevenlabs", "scribe_v1")
if err != nil {
t.Fatalf("GetModel('elevenlabs', 'scribe_v1') error: %v", err)
@@ -357,6 +357,9 @@ func TestElevenLabsProvider(t *testing.T) {
if scribeV1.AdapterType != "elevenlabs" {
t.Errorf("scribe_v1 AdapterType=%q, want 'elevenlabs'", scribeV1.AdapterType)
}
if scribeV1.StreamingAdapter != "elevenlabs-streaming" {
t.Errorf("scribe_v1 StreamingAdapter=%q, want 'elevenlabs-streaming'", scribeV1.StreamingAdapter)
}
scribeV2, err := GetModel("elevenlabs", "scribe_v2")
if err != nil {
@@ -371,8 +374,10 @@ func TestElevenLabsProvider(t *testing.T) {
if scribeV2.AdapterType != "elevenlabs" {
t.Errorf("scribe_v2 AdapterType=%q, want 'elevenlabs'", scribeV2.AdapterType)
}
if scribeV2.StreamingAdapter != "elevenlabs-streaming" {
t.Errorf("scribe_v2 StreamingAdapter=%q, want 'elevenlabs-streaming'", scribeV2.StreamingAdapter)
}
// Check streaming-only model
scribeV2Realtime, err := GetModel("elevenlabs", "scribe_v2_realtime")
if err != nil {
t.Fatalf("GetModel('elevenlabs', 'scribe_v2_realtime') error: %v", err)
@@ -383,8 +388,11 @@ func TestElevenLabsProvider(t *testing.T) {
if !scribeV2Realtime.SupportsStreaming {
t.Error("scribe_v2_realtime should have SupportsStreaming=true")
}
if scribeV2Realtime.AdapterType != "elevenlabs-streaming" {
t.Errorf("scribe_v2_realtime AdapterType=%q, want 'elevenlabs-streaming'", scribeV2Realtime.AdapterType)
if scribeV2Realtime.AdapterType != "elevenlabs" {
t.Errorf("scribe_v2_realtime AdapterType=%q, want 'elevenlabs'", scribeV2Realtime.AdapterType)
}
if scribeV2Realtime.StreamingAdapter != "elevenlabs-streaming" {
t.Errorf("scribe_v2_realtime StreamingAdapter=%q, want 'elevenlabs-streaming'", scribeV2Realtime.StreamingAdapter)
}
// All models should share the same supported language list
+36 -6
View File
@@ -17,16 +17,20 @@ func (p *WhisperCppProvider) ValidateAPIKey(key string) bool {
return true // no API key needed
}
func (p *WhisperCppProvider) APIKeyURL() string {
return ""
}
func (p *WhisperCppProvider) IsLocal() bool {
return true
}
func (p *WhisperCppProvider) Models() []Model {
// https://github.com/openai/whisper#available-models-and-languages
// https://github.com/ggml-org/whisper.cpp#models
allLangs := whisperTranscriptionLanguages
// https://github.com/openai/whisper#available-models-and-languages
// https://github.com/ggml-org/whisper.cpp#models
englishOnly := whisperEnglishOnlyLanguages
docsURL := "https://github.com/openai/whisper#available-models-and-languages"
docsURL := "https://github.com/ggml-org/whisper.cpp#models"
whisperModels := whisper.ListModels()
result := make([]Model, 0, len(whisperModels))
@@ -63,10 +67,36 @@ func (p *WhisperCppProvider) Models() []Model {
}
func modelDescription(m whisper.ModelInfo) string {
if m.Multilingual {
return "Multilingual local transcription"
switch m.ID {
case "tiny.en":
return "Free/offline; fastest but low accuracy, good for weak hardware"
case "base.en":
return "Free/offline; balanced speed and accuracy, recommended start"
case "small.en":
return "Free/offline; better accuracy, needs decent CPU"
case "medium.en":
return "Free/offline; best .en accuracy, needs good CPU/RAM"
case "tiny":
return "Free/offline multilingual; fastest but low accuracy"
case "base":
return "Free/offline multilingual; balanced, recommended start"
case "small":
return "Free/offline multilingual; better accuracy, needs decent CPU"
case "medium":
return "Free/offline multilingual; great accuracy, needs good CPU/RAM"
case "large-v1":
return "Free/offline; high accuracy, needs strong CPU/GPU"
case "large-v2":
return "Free/offline; high accuracy, needs strong CPU/GPU"
case "large-v3":
return "Free/offline; best accuracy available, needs strong hardware"
case "large-v3-turbo":
return "Free/offline; near-best accuracy with better speed"
}
return "English-only local transcription (faster)"
if m.Multilingual {
return "Free/offline multilingual model"
}
return "Free/offline English model"
}
func (p *WhisperCppProvider) DefaultModel(t ModelType) string {
+11 -8
View File
@@ -16,9 +16,9 @@ func TestWhisperCppProvider_Models(t *testing.T) {
p := &WhisperCppProvider{}
models := p.Models()
// verify we have 9 models
if len(models) != 9 {
t.Errorf("expected 9 models, got %d", len(models))
// verify we have 12 models
if len(models) != 12 {
t.Errorf("expected 12 models, got %d", len(models))
}
// verify all models have required fields
@@ -77,11 +77,14 @@ func TestWhisperCppProvider_MultilingualModels(t *testing.T) {
models := p.Models()
multilingualIDs := map[string]bool{
"tiny": true,
"base": true,
"small": true,
"medium": true,
"large-v3": true,
"tiny": true,
"base": true,
"small": true,
"medium": true,
"large-v1": true,
"large-v2": true,
"large-v3": true,
"large-v3-turbo": true,
}
for _, m := range models {