refactor tui to use model metadata for descriptions

- getTranscriptionModelOptions now uses provider.ModelsOfType() instead of hardcoded switch
- getLLMModelOptions now uses provider.ModelsOfType() instead of hardcoded switch
- added currentLang param to show language compatibility warnings on models
- buildModelLabel adds [size] for local models, [streaming] for streaming
- mapConfigProviderToRegistry maps config names to registry names
This commit is contained in:
leonardotrapani
2026-02-01 01:26:02 +01:00
parent c05a3c8c2a
commit 575b55b524
4 changed files with 101 additions and 42 deletions
+18 -16
View File
@@ -8,6 +8,11 @@ import (
"github.com/leonardotrapani/hyprvoice/internal/provider"
)
// buildLLMModelLabel creates the display label for an LLM model option
func buildLLMModelLabel(m provider.Model) string {
return fmt.Sprintf("%s (%s)", m.Name, m.Description)
}
// editLLM handles the LLM section edit with smart provider detection
func editLLM(cfg *config.Config, configuredProviders []string) ([]string, error) {
var llmProviders []string
@@ -210,24 +215,21 @@ func getUnconfiguredLLMOptions(configuredProviders []string) []huh.Option[string
return options
}
func getLLMModelOptions(provider string) []huh.Option[string] {
switch provider {
case "openai":
return []huh.Option[string]{
huh.NewOption("gpt-4o-mini (recommended)", "gpt-4o-mini"),
huh.NewOption("gpt-4o", "gpt-4o"),
huh.NewOption("gpt-4-turbo", "gpt-4-turbo"),
huh.NewOption("gpt-3.5-turbo", "gpt-3.5-turbo"),
}
case "groq":
return []huh.Option[string]{
huh.NewOption("llama-3.3-70b-versatile (recommended)", "llama-3.3-70b-versatile"),
huh.NewOption("llama-3.1-8b-instant (faster)", "llama-3.1-8b-instant"),
huh.NewOption("mixtral-8x7b-32768", "mixtral-8x7b-32768"),
}
default:
func getLLMModelOptions(providerName string) []huh.Option[string] {
p := provider.GetProvider(providerName)
if p == nil {
return []huh.Option[string]{}
}
models := provider.ModelsOfType(p, provider.LLM)
var options []huh.Option[string]
for _, m := range models {
label := buildLLMModelLabel(m)
options = append(options, huh.NewOption(label, m.ID))
}
return options
}
// selectPostProcessingOptions shows a multi-select for LLM post-processing toggles
+70 -24
View File
@@ -5,6 +5,7 @@ import (
"github.com/charmbracelet/huh"
"github.com/leonardotrapani/hyprvoice/internal/config"
"github.com/leonardotrapani/hyprvoice/internal/language"
"github.com/leonardotrapani/hyprvoice/internal/provider"
)
@@ -68,7 +69,7 @@ func editTranscription(cfg *config.Config, configuredProviders []string) ([]stri
configuredProviders = ensureProviderConfigured(cfg, selectedProvider, configuredProviders)
cfg.Transcription.Provider = selectedProvider
modelOptions := getTranscriptionModelOptions(selectedProvider)
modelOptions := getTranscriptionModelOptions(selectedProvider, cfg.Transcription.Language)
selectedModel := cfg.Transcription.Model
if selectedModel == "" && len(modelOptions) > 0 {
selectedModel = modelOptions[0].Value
@@ -136,32 +137,77 @@ func getUnconfiguredTranscriptionOptions(configuredProviders []string) []huh.Opt
return options
}
func getTranscriptionModelOptions(provider string) []huh.Option[string] {
switch provider {
case "openai":
return []huh.Option[string]{
huh.NewOption("whisper-1", "whisper-1"),
}
case "groq-transcription":
return []huh.Option[string]{
huh.NewOption("whisper-large-v3-turbo (faster)", "whisper-large-v3-turbo"),
huh.NewOption("whisper-large-v3 (standard)", "whisper-large-v3"),
}
case "groq-translation":
func getTranscriptionModelOptions(configProvider string, currentLang string) []huh.Option[string] {
// special case: groq-translation only supports whisper-large-v3
if configProvider == "groq-translation" {
return []huh.Option[string]{
huh.NewOption("whisper-large-v3 (only option)", "whisper-large-v3"),
}
case "mistral-transcription":
return []huh.Option[string]{
huh.NewOption("voxtral-mini-latest (recommended)", "voxtral-mini-latest"),
huh.NewOption("voxtral-mini-2507", "voxtral-mini-2507"),
}
case "elevenlabs":
return []huh.Option[string]{
huh.NewOption("scribe_v1 (99 languages, best accuracy)", "scribe_v1"),
huh.NewOption("scribe_v2 (real-time, lower latency)", "scribe_v2"),
}
default:
}
// map config provider name to registry provider name
registryName := mapConfigProviderToRegistry(configProvider)
p := provider.GetProvider(registryName)
if p == nil {
return []huh.Option[string]{}
}
models := provider.ModelsOfType(p, provider.Transcription)
var options []huh.Option[string]
for _, m := range models {
// skip streaming models for now (not yet implemented)
if m.Streaming {
continue
}
label := buildModelLabel(m, currentLang)
options = append(options, huh.NewOption(label, m.ID))
}
return options
}
// mapConfigProviderToRegistry maps config provider names to registry provider names
func mapConfigProviderToRegistry(configProvider string) string {
switch configProvider {
case "groq-transcription", "groq-translation":
return "groq"
case "mistral-transcription":
return "mistral"
default:
return configProvider
}
}
// buildModelLabel creates the display label for a model option
func buildModelLabel(m provider.Model, currentLang string) string {
label := fmt.Sprintf("%s (%s)", m.Name, m.Description)
// append size for local models
if m.Local && m.LocalInfo != nil {
label += fmt.Sprintf(" [%s]", m.LocalInfo.Size)
}
// append streaming tag
if m.Streaming {
label += " [streaming]"
}
// append language warning if model doesn't support current language
if currentLang != "" && !m.SupportsLanguage(currentLang) {
langName := getLangName(currentLang)
label += fmt.Sprintf(" (does not support %s)", langName)
}
return label
}
// getLangName returns a human-readable language name for a code
func getLangName(code string) string {
lang := language.FromCode(code)
if lang.Code == "" {
return code // unknown code, return as-is
}
return lang.Name
}
+12 -1
View File
@@ -263,4 +263,15 @@ Started: Sun Feb 1 12:22:47 AM CET 2026
- Cloud models: prints 'nothing to remove'
- Not installed: returns error 'model is not installed'
- Installed: calls `whisper.Remove()`, prints success message
- All verification scenarios tested, typecheck passes
- All verification scenarios tested, typecheck passes
### Task 27: Refactor TUI to use Model metadata for descriptions
- Refactored `getTranscriptionModelOptions()` to use `provider.ModelsOfType()` instead of hardcoded switch
- Added `currentLang` parameter to show language compatibility warnings
- Created `buildModelLabel()` helper: formats "Name (Description)", adds [size] for local, [streaming] for streaming models
- Created `mapConfigProviderToRegistry()` to map config provider names (groq-transcription, mistral-transcription) to registry names
- Refactored `getLLMModelOptions()` to use `provider.ModelsOfType()` instead of hardcoded switch
- Created `buildLLMModelLabel()` helper for LLM model formatting
- Added `getLangName()` helper to get human-readable language name from code
- Added language import to configure_transcription.go
- All tests passing, typecheck passes
+1 -1
View File
@@ -644,7 +644,7 @@
"No more hardcoded descriptions in TUI",
"Typecheck passes"
],
"passes": false
"passes": true
},
{
"title": "Add local provider options to TUI with dependency check",