refactor provider interface to return models with metadata

- replaced old TranscriptionModels/LLMModels/SupportsX methods with Models() []Model
- added DefaultModel(t ModelType), IsLocal() to interface
- added helpers: GetModel, ModelsOfType, FindModelByID, ModelsForLanguage, ValidateModelLanguage
- all providers now return full Model metadata with SupportedLanguages, AdapterType, Endpoint
- groq distil-whisper-large-v3-en marked as english-only
- updated TUI to use new interface
This commit is contained in:
leonardotrapani
2026-02-01 00:33:20 +01:00
parent 51aba743f8
commit c23ac60b8f
10 changed files with 498 additions and 98 deletions
+140 -17
View File
@@ -10,13 +10,14 @@ func TestProviderInterface(t *testing.T) {
name string
hasTranscription bool
hasLLM bool
isLocal bool
defaultTransModel string
defaultLLMModel string
}{
{"openai", true, true, "whisper-1", "gpt-4o-mini"},
{"groq", true, true, "whisper-large-v3-turbo", "llama-3.3-70b-versatile"},
{"mistral", true, false, "voxtral-mini-latest", ""},
{"elevenlabs", true, false, "scribe_v1", ""},
{"openai", true, true, false, "whisper-1", "gpt-4o-mini"},
{"groq", true, true, false, "whisper-large-v3-turbo", "llama-3.3-70b-versatile"},
{"mistral", true, false, false, "voxtral-mini-latest", ""},
{"elevenlabs", true, false, false, "scribe_v1", ""},
}
for _, tc := range providers {
@@ -30,32 +31,38 @@ func TestProviderInterface(t *testing.T) {
t.Errorf("Name() = %q, want %q", p.Name(), tc.name)
}
if p.SupportsTranscription() != tc.hasTranscription {
t.Errorf("SupportsTranscription() = %v, want %v", p.SupportsTranscription(), tc.hasTranscription)
hasTranscription := len(ModelsOfType(p, Transcription)) > 0
if hasTranscription != tc.hasTranscription {
t.Errorf("hasTranscription = %v, want %v", hasTranscription, tc.hasTranscription)
}
if p.SupportsLLM() != tc.hasLLM {
t.Errorf("SupportsLLM() = %v, want %v", p.SupportsLLM(), tc.hasLLM)
hasLLM := len(ModelsOfType(p, LLM)) > 0
if hasLLM != tc.hasLLM {
t.Errorf("hasLLM = %v, want %v", hasLLM, tc.hasLLM)
}
if p.DefaultTranscriptionModel() != tc.defaultTransModel {
t.Errorf("DefaultTranscriptionModel() = %q, want %q", p.DefaultTranscriptionModel(), tc.defaultTransModel)
if p.IsLocal() != tc.isLocal {
t.Errorf("IsLocal() = %v, want %v", p.IsLocal(), tc.isLocal)
}
if p.DefaultLLMModel() != tc.defaultLLMModel {
t.Errorf("DefaultLLMModel() = %q, want %q", p.DefaultLLMModel(), tc.defaultLLMModel)
if p.DefaultModel(Transcription) != tc.defaultTransModel {
t.Errorf("DefaultModel(Transcription) = %q, want %q", p.DefaultModel(Transcription), tc.defaultTransModel)
}
if p.DefaultModel(LLM) != tc.defaultLLMModel {
t.Errorf("DefaultModel(LLM) = %q, want %q", p.DefaultModel(LLM), tc.defaultLLMModel)
}
if !p.RequiresAPIKey() {
t.Error("RequiresAPIKey() should be true for all providers")
t.Error("RequiresAPIKey() should be true for all cloud providers")
}
if tc.hasTranscription && len(p.TranscriptionModels()) == 0 {
t.Error("TranscriptionModels() should not be empty for transcription provider")
if tc.hasTranscription && len(ModelsOfType(p, Transcription)) == 0 {
t.Error("should have transcription models")
}
if tc.hasLLM && len(p.LLMModels()) == 0 {
t.Error("LLMModels() should not be empty for LLM provider")
if tc.hasLLM && len(ModelsOfType(p, LLM)) == 0 {
t.Error("should have LLM models")
}
})
}
@@ -137,3 +144,119 @@ func TestValidateAPIKey(t *testing.T) {
})
}
}
func TestGetModel(t *testing.T) {
// valid provider and model
m, err := GetModel("openai", "whisper-1")
if err != nil {
t.Errorf("GetModel('openai', 'whisper-1') unexpected error: %v", err)
}
if m == nil {
t.Fatal("GetModel returned nil model")
}
if m.ID != "whisper-1" {
t.Errorf("GetModel returned model with ID %q, want 'whisper-1'", m.ID)
}
// unknown provider
_, err = GetModel("nonexistent", "whisper-1")
if err == nil {
t.Error("GetModel('nonexistent', ...) should return error")
}
// unknown model
_, err = GetModel("openai", "nonexistent")
if err == nil {
t.Error("GetModel('openai', 'nonexistent') should return error")
}
}
func TestModelsOfType(t *testing.T) {
p := GetProvider("openai")
trans := ModelsOfType(p, Transcription)
llm := ModelsOfType(p, LLM)
if len(trans) != 1 {
t.Errorf("ModelsOfType(Transcription) = %d, want 1", len(trans))
}
if len(llm) != 4 {
t.Errorf("ModelsOfType(LLM) = %d, want 4", len(llm))
}
}
func TestFindModelByID(t *testing.T) {
// find model that exists
m, p, err := FindModelByID("whisper-1")
if err != nil {
t.Errorf("FindModelByID('whisper-1') unexpected error: %v", err)
}
if m == nil || p == nil {
t.Fatal("FindModelByID returned nil")
}
if m.ID != "whisper-1" {
t.Errorf("FindModelByID returned model %q, want 'whisper-1'", m.ID)
}
if p.Name() != "openai" {
t.Errorf("FindModelByID returned provider %q, want 'openai'", p.Name())
}
// model not found
_, _, err = FindModelByID("nonexistent")
if err == nil {
t.Error("FindModelByID('nonexistent') should return error")
}
}
func TestModelsForLanguage(t *testing.T) {
groq := GetProvider("groq")
// en should include all models (distil supports en)
enModels := ModelsForLanguage(groq, Transcription, "en")
if len(enModels) != 3 {
t.Errorf("ModelsForLanguage('en') = %d, want 3", len(enModels))
}
// es should exclude distil-whisper-large-v3-en
esModels := ModelsForLanguage(groq, Transcription, "es")
if len(esModels) != 2 {
t.Errorf("ModelsForLanguage('es') = %d, want 2 (distil excluded)", len(esModels))
}
// auto ("") should include all models
autoModels := ModelsForLanguage(groq, Transcription, "")
if len(autoModels) != 3 {
t.Errorf("ModelsForLanguage('') = %d, want 3 (auto returns all)", len(autoModels))
}
}
func TestValidateModelLanguage(t *testing.T) {
// valid language for multilingual model
err := ValidateModelLanguage("groq", "whisper-large-v3", "es")
if err != nil {
t.Errorf("ValidateModelLanguage(whisper-large-v3, 'es') unexpected error: %v", err)
}
// invalid language for English-only model
err = ValidateModelLanguage("groq", "distil-whisper-large-v3-en", "es")
if err == nil {
t.Error("ValidateModelLanguage(distil-whisper, 'es') should return error")
}
// auto always passes
err = ValidateModelLanguage("groq", "distil-whisper-large-v3-en", "")
if err != nil {
t.Errorf("ValidateModelLanguage(distil-whisper, '') should pass (auto): %v", err)
}
// unknown provider
err = ValidateModelLanguage("nonexistent", "whisper-1", "en")
if err == nil {
t.Error("ValidateModelLanguage with unknown provider should return error")
}
// unknown model
err = ValidateModelLanguage("openai", "nonexistent", "en")
if err == nil {
t.Error("ValidateModelLanguage with unknown model should return error")
}
}