From 575b55b5248393834e02f9c7ee0a68c2eac72157 Mon Sep 17 00:00:00 2001 From: leonardotrapani Date: Sun, 1 Feb 2026 01:26:02 +0100 Subject: [PATCH] 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 --- internal/tui/configure_llm.go | 34 ++++----- internal/tui/configure_transcription.go | 94 ++++++++++++++++++------- progress.txt | 13 +++- tasks/prd.jsonc | 2 +- 4 files changed, 101 insertions(+), 42 deletions(-) diff --git a/internal/tui/configure_llm.go b/internal/tui/configure_llm.go index c274da0..da4de70 100644 --- a/internal/tui/configure_llm.go +++ b/internal/tui/configure_llm.go @@ -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 diff --git a/internal/tui/configure_transcription.go b/internal/tui/configure_transcription.go index e1fdb99..3f9ca7e 100644 --- a/internal/tui/configure_transcription.go +++ b/internal/tui/configure_transcription.go @@ -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 } diff --git a/progress.txt b/progress.txt index add4920..f96d6e6 100644 --- a/progress.txt +++ b/progress.txt @@ -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 \ No newline at end of file +- 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 \ No newline at end of file diff --git a/tasks/prd.jsonc b/tasks/prd.jsonc index 57830f8..f178675 100644 --- a/tasks/prd.jsonc +++ b/tasks/prd.jsonc @@ -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",