+63
-27
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/leonardotrapani/hyprvoice/internal/provider"
|
||||
@@ -34,6 +35,8 @@ func envVarForProvider(registryName string) string {
|
||||
return "ELEVENLABS_API_KEY"
|
||||
case "deepgram":
|
||||
return "DEEPGRAM_API_KEY"
|
||||
case "llama-swap":
|
||||
return "LLAMA_SWAP_API_KEY"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
@@ -82,27 +85,40 @@ func (c *Config) Validate() error {
|
||||
strings.Title(registryName), registryName, envVar)
|
||||
}
|
||||
}
|
||||
if registryName == provider.ProviderLlamaSwap {
|
||||
if err := validateLlamaSwapBaseURL(c.Providers[provider.ProviderLlamaSwap].BaseURL); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// validate model exists
|
||||
if c.Transcription.Model == "" {
|
||||
return fmt.Errorf("invalid transcription.model: empty")
|
||||
}
|
||||
|
||||
// validate model exists in provider
|
||||
_, err := provider.GetModel(registryName, c.Transcription.Model)
|
||||
if err != nil {
|
||||
models := provider.ModelsOfType(p, provider.Transcription)
|
||||
modelIDs := make([]string, len(models))
|
||||
for i, m := range models {
|
||||
modelIDs[i] = m.ID
|
||||
// LlamaSwap is an OpenAI-compatible router: its model IDs are defined by the
|
||||
// remote server, so they cannot be validated against Hyprvoice's static registry.
|
||||
if registryName == provider.ProviderLlamaSwap {
|
||||
if c.Transcription.Streaming {
|
||||
return fmt.Errorf("llama-swap transcription supports batch mode only (set transcription.streaming = false)")
|
||||
}
|
||||
} else {
|
||||
// validate model exists in provider
|
||||
_, err := provider.GetModel(registryName, c.Transcription.Model)
|
||||
if err != nil {
|
||||
models := provider.ModelsOfType(p, provider.Transcription)
|
||||
modelIDs := make([]string, len(models))
|
||||
for i, m := range models {
|
||||
modelIDs[i] = m.ID
|
||||
}
|
||||
return fmt.Errorf("invalid model for %s: %s (available: %s)", c.Transcription.Provider, c.Transcription.Model, strings.Join(modelIDs, ", "))
|
||||
}
|
||||
return fmt.Errorf("invalid model for %s: %s (available: %s)", c.Transcription.Provider, c.Transcription.Model, strings.Join(modelIDs, ", "))
|
||||
}
|
||||
|
||||
// validate language-model compatibility using effective language (transcription overrides general)
|
||||
effectiveLanguage := c.resolveEffectiveLanguage()
|
||||
if err := ValidateModelLanguageCompatibility(registryName, c.Transcription.Model, effectiveLanguage); err != nil {
|
||||
return err
|
||||
// validate language-model compatibility using effective language (transcription overrides general)
|
||||
effectiveLanguage := c.resolveEffectiveLanguage()
|
||||
if err := ValidateModelLanguageCompatibility(registryName, c.Transcription.Model, effectiveLanguage); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// LLM validation
|
||||
@@ -121,20 +137,26 @@ func (c *Config) Validate() error {
|
||||
return fmt.Errorf("invalid llm.provider: %s (available: %s)", c.LLM.Provider, strings.Join(providers, ", "))
|
||||
}
|
||||
|
||||
// validate LLM model exists
|
||||
llmModel, err := provider.GetModel(c.LLM.Provider, c.LLM.Model)
|
||||
if err != nil {
|
||||
models := provider.ModelsOfType(llmProvider, provider.LLM)
|
||||
modelIDs := make([]string, len(models))
|
||||
for i, m := range models {
|
||||
modelIDs[i] = m.ID
|
||||
if c.LLM.Provider == provider.ProviderLlamaSwap {
|
||||
if err := validateLlamaSwapBaseURL(c.Providers[provider.ProviderLlamaSwap].BaseURL); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// validate LLM model exists
|
||||
llmModel, err := provider.GetModel(c.LLM.Provider, c.LLM.Model)
|
||||
if err != nil {
|
||||
models := provider.ModelsOfType(llmProvider, provider.LLM)
|
||||
modelIDs := make([]string, len(models))
|
||||
for i, m := range models {
|
||||
modelIDs[i] = m.ID
|
||||
}
|
||||
return fmt.Errorf("invalid llm.model: %s (available for %s: %s)", c.LLM.Model, c.LLM.Provider, strings.Join(modelIDs, ", "))
|
||||
}
|
||||
return fmt.Errorf("invalid llm.model: %s (available for %s: %s)", c.LLM.Model, c.LLM.Provider, strings.Join(modelIDs, ", "))
|
||||
}
|
||||
|
||||
// verify model is actually an LLM
|
||||
if llmModel.Type != provider.LLM {
|
||||
return fmt.Errorf("invalid llm.model: %s is not an LLM model", c.LLM.Model)
|
||||
// verify model is actually an LLM
|
||||
if llmModel.Type != provider.LLM {
|
||||
return fmt.Errorf("invalid llm.model: %s is not an LLM model", c.LLM.Model)
|
||||
}
|
||||
}
|
||||
|
||||
// validate LLM API key
|
||||
@@ -151,10 +173,10 @@ func (c *Config) Validate() error {
|
||||
if len(c.Injection.Backends) == 0 {
|
||||
return fmt.Errorf("invalid injection.backends: empty (must have at least one backend)")
|
||||
}
|
||||
validBackends := map[string]bool{"ydotool": true, "wtype": true, "clipboard": true}
|
||||
validBackends := map[string]bool{"clipboard-paste": true, "ydotool": true, "wtype": true, "clipboard": true}
|
||||
for _, backend := range c.Injection.Backends {
|
||||
if !validBackends[backend] {
|
||||
return fmt.Errorf("invalid injection.backends: unknown backend %q (must be ydotool, wtype, or clipboard)", backend)
|
||||
return fmt.Errorf("invalid injection.backends: unknown backend %q (must be clipboard-paste, ydotool, wtype, or clipboard)", backend)
|
||||
}
|
||||
}
|
||||
if c.Injection.YdotoolTimeout <= 0 {
|
||||
@@ -175,6 +197,20 @@ func (c *Config) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateLlamaSwapBaseURL(baseURL string) error {
|
||||
if baseURL == "" {
|
||||
return fmt.Errorf("llama-swap base_url required in providers.llama-swap.base_url")
|
||||
}
|
||||
u, err := url.Parse(baseURL)
|
||||
if err != nil || u.Scheme == "" || u.Host == "" {
|
||||
return fmt.Errorf("invalid llama-swap base_url: %q (expected http://host:port, without /v1)", baseURL)
|
||||
}
|
||||
if strings.TrimRight(u.Path, "/") == "/v1" {
|
||||
return fmt.Errorf("invalid llama-swap base_url: omit the /v1 suffix")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateModelLanguageCompatibility validates that a model supports the given language.
|
||||
// Returns error if the language is not supported, nil if supported or if langCode is empty (auto).
|
||||
func ValidateModelLanguageCompatibility(registryProvider, modelID, langCode string) error {
|
||||
|
||||
Reference in New Issue
Block a user