From 30ce13b7c17bddf726736a34d0415a3a4ea8b09a Mon Sep 17 00:00:00 2001 From: leonardotrapani Date: Mon, 2 Feb 2026 10:44:15 +0100 Subject: [PATCH] move test-models from cli to integration tests - removes test-models command to reduce binary size - adds integration_test.go with build tag (go test -tags=integration) - tests all provider/model combinations with language/keywords variations - adds testdata/sample.wav for deterministic testing - updates e2e.yml workflow to use go test instead of cli - uses go-version-file in all workflows for consistency --- .github/workflows/ci.yml | 2 +- .github/workflows/e2e.yml | 31 +- .github/workflows/release.yml | 2 +- cmd/hyprvoice/integration_test.go | 541 +++++++++++++++++++ cmd/hyprvoice/main.go | 1 - cmd/hyprvoice/test_models.go | 865 ------------------------------ testdata/sample.wav | Bin 0 -> 93638 bytes 7 files changed, 548 insertions(+), 894 deletions(-) create mode 100644 cmd/hyprvoice/integration_test.go delete mode 100644 cmd/hyprvoice/test_models.go create mode 100644 testdata/sample.wav diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 638f26b..95f5a88 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.24" + go-version-file: go.mod - name: Install dependencies run: | diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index fa1b24a..c05a178 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -3,15 +3,10 @@ name: E2E Tests on: workflow_dispatch: - inputs: - timeout: - description: 'Per-model timeout (e.g. 60s)' - required: false - default: '60s' jobs: - test-models: - name: Test All Models + integration: + name: Integration Tests runs-on: ubuntu-latest steps: - name: Checkout code @@ -20,7 +15,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.24" + go-version-file: go.mod - name: Install dependencies run: | @@ -35,27 +30,11 @@ jobs: - name: Download dependencies run: go mod download - - name: Build binary - env: - CGO_ENABLED: 1 - run: go build -o hyprvoice ./cmd/hyprvoice - - - name: Run test-models + - name: Run integration tests env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} DEEPGRAM_API_KEY: ${{ secrets.DEEPGRAM_API_KEY }} GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} ELEVENLABS_API_KEY: ${{ secrets.ELEVENLABS_API_KEY }} MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} - run: | - ./hyprvoice test-models \ - --timeout=${{ inputs.timeout }} \ - --output=test-models-report.json - - - name: Upload report - uses: actions/upload-artifact@v4 - if: always() - with: - name: test-models-report - path: test-models-report.json - retention-days: 30 + run: go test -tags=integration -v ./cmd/hyprvoice -timeout 15m diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9ee3308..4a416e0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.24" + go-version-file: go.mod - name: Install dependencies run: | diff --git a/cmd/hyprvoice/integration_test.go b/cmd/hyprvoice/integration_test.go new file mode 100644 index 0000000..f421f08 --- /dev/null +++ b/cmd/hyprvoice/integration_test.go @@ -0,0 +1,541 @@ +//go:build integration + +package main + +import ( + "context" + "encoding/binary" + "errors" + "fmt" + "math" + "os" + "os/exec" + "path/filepath" + "runtime" + "sort" + "strings" + "testing" + "time" + + "github.com/leonardotrapani/hyprvoice/internal/config" + "github.com/leonardotrapani/hyprvoice/internal/llm" + "github.com/leonardotrapani/hyprvoice/internal/models/whisper" + "github.com/leonardotrapani/hyprvoice/internal/provider" + "github.com/leonardotrapani/hyprvoice/internal/recording" + "github.com/leonardotrapani/hyprvoice/internal/transcriber" +) + +const ( + testSampleRate = 16000 + testChannels = 1 + testBitsPerSample = 16 + testTimeout = 45 * time.Second +) + +var testKeywords = []string{"Hyprvoice", "transcription", "dictation"} + +func TestTranscriptionModels(t *testing.T) { + audio, err := loadTestAudio(t) + if err != nil { + t.Fatalf("failed to load test audio: %v", err) + } + + cfg := loadTestConfig(t) + + providerNames := provider.ListProvidersWithTranscription() + sort.Strings(providerNames) + + smallestLocalModel := selectSmallestLocalModel() + + for _, providerName := range providerNames { + p := provider.GetProvider(providerName) + if p == nil { + continue + } + + models := provider.ModelsOfType(p, provider.Transcription) + sort.Slice(models, func(i, j int) bool { + return models[i].ID < models[j].ID + }) + + for _, model := range models { + if model.Local && providerName == provider.ProviderWhisperCpp && model.ID != smallestLocalModel { + continue + } + + modes := getModesForModel(model) + languages := []string{"en", ""} + keywordOptions := []bool{true, false} + + for _, mode := range modes { + for _, lang := range languages { + for _, useKeywords := range keywordOptions { + testName := fmt.Sprintf("%s/%s/%s/lang=%s/keywords=%v", + providerName, model.ID, mode, langDisplay(lang), useKeywords) + + model := model + mode := mode + lang := lang + useKeywords := useKeywords + providerName := providerName + + t.Run(testName, func(t *testing.T) { + t.Parallel() + runTranscriptionTest(t, cfg, providerName, model, mode, lang, useKeywords, audio) + }) + } + } + } + } + } +} + +func TestLLMModels(t *testing.T) { + cfg := loadTestConfig(t) + + providerNames := provider.ListProvidersWithLLM() + sort.Strings(providerNames) + + for _, providerName := range providerNames { + p := provider.GetProvider(providerName) + if p == nil { + continue + } + + models := provider.ModelsOfType(p, provider.LLM) + sort.Slice(models, func(i, j int) bool { + return models[i].ID < models[j].ID + }) + + for _, model := range models { + for _, useKeywords := range []bool{true, false} { + testName := fmt.Sprintf("%s/%s/keywords=%v", providerName, model.ID, useKeywords) + + model := model + useKeywords := useKeywords + providerName := providerName + + t.Run(testName, func(t *testing.T) { + t.Parallel() + runLLMTest(t, cfg, providerName, model, useKeywords) + }) + } + } + } +} + +func runTranscriptionTest(t *testing.T, cfg *config.Config, providerName string, model provider.Model, mode, lang string, useKeywords bool, audio []byte) { + if model.Local { + if _, err := exec.LookPath("whisper-cli"); err != nil { + t.Skip("whisper-cli not found") + } + if !whisper.IsInstalled(model.ID) { + t.Skipf("local model %s not installed", model.ID) + } + } + + apiKey := resolveTestAPIKey(cfg, providerName) + if testProviderRequiresKey(providerName) && apiKey == "" { + t.Skipf("missing api key for %s", providerName) + } + + var keywords []string + if useKeywords { + keywords = testKeywords + } + + streaming := mode == "streaming" + transcribeCfg := transcriber.Config{ + Provider: providerName, + APIKey: apiKey, + Language: lang, + Model: model.ID, + Keywords: keywords, + Threads: 0, + Streaming: streaming, + } + + ctx, cancel := context.WithTimeout(context.Background(), testTimeout) + defer cancel() + + text, err := runTestTranscriber(ctx, transcribeCfg, audio) + if err != nil { + t.Errorf("transcription failed: %v", err) + return + } + + text = strings.TrimSpace(text) + if text == "" { + t.Error("transcription returned empty text") + return + } + + t.Logf("output (%d chars): %q", len(text), truncateTestString(text, 100)) +} + +func runLLMTest(t *testing.T, cfg *config.Config, providerName string, model provider.Model, useKeywords bool) { + apiKey := resolveTestAPIKey(cfg, providerName) + if testProviderRequiresKey(providerName) && apiKey == "" { + t.Skipf("missing api key for %s", providerName) + } + + var keywords []string + if useKeywords { + keywords = testKeywords + } + + llmCfg := llm.Config{ + Provider: providerName, + APIKey: apiKey, + Model: model.ID, + RemoveStutters: true, + AddPunctuation: true, + FixGrammar: true, + RemoveFillerWords: true, + CustomPrompt: "", + Keywords: keywords, + } + + adapter, err := llm.NewAdapter(llmCfg) + if err != nil { + t.Errorf("failed to create adapter: %v", err) + return + } + + input := "uh i i i want to test hyprvoice you know this is just a cleanup check" + ctx, cancel := context.WithTimeout(context.Background(), testTimeout) + defer cancel() + + output, err := adapter.Process(ctx, input) + if err != nil { + t.Errorf("llm processing failed: %v", err) + return + } + + output = strings.TrimSpace(output) + if output == "" { + t.Error("llm returned empty output") + return + } + + t.Logf("output (%d chars): %q", len(output), truncateTestString(output, 100)) +} + +func runTestTranscriber(ctx context.Context, cfg transcriber.Config, audio []byte) (string, error) { + tr, err := transcriber.NewTranscriber(cfg) + if err != nil { + return "", err + } + + frameCh := make(chan recording.AudioFrame, 8) + errCh, err := tr.Start(ctx, frameCh) + if err != nil { + return "", err + } + + sendErr := sendTestAudioFrames(ctx, frameCh, audio) + close(frameCh) + + stopErr := tr.Stop(ctx) + errChErr := readTestErrorChannel(errCh) + + if sendErr != nil { + return "", sendErr + } + if stopErr != nil { + return "", stopErr + } + if errChErr != nil { + return "", errChErr + } + + return tr.GetFinalTranscription() +} + +func sendTestAudioFrames(ctx context.Context, frameCh chan<- recording.AudioFrame, audio []byte) error { + const chunkBytes = 3200 + bytesPerSecond := testSampleRate * (testBitsPerSample / 8) * testChannels + chunkDuration := time.Duration(float64(chunkBytes) / float64(bytesPerSecond) * float64(time.Second)) + + for offset := 0; offset < len(audio); offset += chunkBytes { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + end := offset + chunkBytes + if end > len(audio) { + end = len(audio) + } + + frame := recording.AudioFrame{Data: audio[offset:end], Timestamp: time.Now()} + select { + case frameCh <- frame: + case <-ctx.Done(): + return ctx.Err() + } + + time.Sleep(chunkDuration) + } + + return nil +} + +func readTestErrorChannel(errCh <-chan error) error { + if errCh == nil { + return nil + } + + var firstErr error + idleTimer := time.NewTimer(150 * time.Millisecond) + defer idleTimer.Stop() + + for { + select { + case err, ok := <-errCh: + if !ok { + return firstErr + } + if err != nil && firstErr == nil { + firstErr = err + } + if !idleTimer.Stop() { + <-idleTimer.C + } + idleTimer.Reset(150 * time.Millisecond) + case <-idleTimer.C: + return firstErr + } + } +} + +func loadTestAudio(t *testing.T) ([]byte, error) { + _, currentFile, _, ok := runtime.Caller(0) + if !ok { + return nil, fmt.Errorf("could not determine current file path") + } + + projectRoot := filepath.Dir(filepath.Dir(filepath.Dir(currentFile))) + samplePath := filepath.Join(projectRoot, "testdata", "sample.wav") + + data, err := os.ReadFile(samplePath) + if err != nil { + return nil, fmt.Errorf("could not read sample audio: %w", err) + } + + return parseTestWAV(data) +} + +func parseTestWAV(data []byte) ([]byte, error) { + if len(data) < 12 { + return nil, fmt.Errorf("invalid wav: too short") + } + if string(data[0:4]) != "RIFF" || string(data[8:12]) != "WAVE" { + return nil, fmt.Errorf("invalid wav: missing riff/wave header") + } + + offset := 12 + var fmtFound, dataFound bool + var sampleRate, channels, bitsPerSample int + var audioData []byte + + for offset+8 <= len(data) { + chunkID := string(data[offset : offset+4]) + chunkSize := int(binary.LittleEndian.Uint32(data[offset+4 : offset+8])) + offset += 8 + if offset+chunkSize > len(data) { + return nil, fmt.Errorf("invalid wav: chunk overflows file") + } + + switch chunkID { + case "fmt ": + if chunkSize < 16 { + return nil, fmt.Errorf("invalid wav: fmt chunk too short") + } + audioFormat := binary.LittleEndian.Uint16(data[offset : offset+2]) + if audioFormat != 1 { + return nil, fmt.Errorf("unsupported wav format: %d", audioFormat) + } + channels = int(binary.LittleEndian.Uint16(data[offset+2 : offset+4])) + sampleRate = int(binary.LittleEndian.Uint32(data[offset+4 : offset+8])) + bitsPerSample = int(binary.LittleEndian.Uint16(data[offset+14 : offset+16])) + fmtFound = true + case "data": + audioData = data[offset : offset+chunkSize] + dataFound = true + } + + offset += chunkSize + if chunkSize%2 == 1 { + offset++ + } + } + + if !fmtFound || !dataFound { + return nil, fmt.Errorf("invalid wav: missing fmt or data chunk") + } + if bitsPerSample != testBitsPerSample { + return nil, fmt.Errorf("unsupported wav bits per sample: %d", bitsPerSample) + } + + monoData, err := downmixTestToMono(audioData, channels) + if err != nil { + return nil, err + } + resampled := resampleTestPCM16(monoData, sampleRate, testSampleRate) + if len(resampled) == 0 { + return nil, fmt.Errorf("invalid wav: empty audio data") + } + + return resampled, nil +} + +func downmixTestToMono(data []byte, channels int) ([]byte, error) { + if channels == 1 { + return data, nil + } + if channels <= 0 { + return nil, fmt.Errorf("invalid channel count: %d", channels) + } + frameSize := 2 * channels + if len(data)%frameSize != 0 { + return nil, fmt.Errorf("invalid pcm data length") + } + + frames := len(data) / frameSize + out := make([]byte, frames*2) + for i := 0; i < frames; i++ { + var sum int32 + for c := 0; c < channels; c++ { + idx := (i*channels + c) * 2 + sample := int16(binary.LittleEndian.Uint16(data[idx : idx+2])) + sum += int32(sample) + } + mono := int16(sum / int32(channels)) + out[i*2] = byte(mono) + out[i*2+1] = byte(mono >> 8) + } + + return out, nil +} + +func resampleTestPCM16(data []byte, inRate, outRate int) []byte { + if inRate <= 0 || outRate <= 0 || inRate == outRate { + return data + } + if len(data) < 2 { + return data + } + + numInSamples := len(data) / 2 + numOutSamples := int(math.Round(float64(numInSamples) * float64(outRate) / float64(inRate))) + if numOutSamples <= 0 { + return nil + } + + out := make([]byte, numOutSamples*2) + for i := 0; i < numOutSamples; i++ { + srcPos := float64(i) * float64(inRate) / float64(outRate) + srcIdx := int(srcPos) + frac := srcPos - float64(srcIdx) + + sample1 := sampleTestAtPCM16(data, srcIdx) + sample2 := sampleTestAtPCM16(data, srcIdx+1) + outSample := int16(float64(sample1)*(1-frac) + float64(sample2)*frac) + + out[i*2] = byte(outSample) + out[i*2+1] = byte(outSample >> 8) + } + + return out +} + +func sampleTestAtPCM16(data []byte, idx int) int16 { + if idx <= 0 { + return int16(binary.LittleEndian.Uint16(data[0:2])) + } + pos := idx * 2 + if pos+1 >= len(data) { + last := len(data) - 2 + if last < 0 { + return 0 + } + return int16(binary.LittleEndian.Uint16(data[last : last+2])) + } + return int16(binary.LittleEndian.Uint16(data[pos : pos+2])) +} + +func loadTestConfig(t *testing.T) *config.Config { + cfg, err := config.Load() + if err != nil { + if errors.Is(err, config.ErrConfigNotFound) { + return config.DefaultConfig() + } + t.Logf("warning: could not load config: %v", err) + return config.DefaultConfig() + } + if cfg.Providers == nil { + cfg.Providers = make(map[string]config.ProviderConfig) + } + return cfg +} + +func resolveTestAPIKey(cfg *config.Config, providerName string) string { + base := provider.BaseProviderName(providerName) + if cfg != nil && cfg.Providers != nil { + if pc, ok := cfg.Providers[base]; ok && pc.APIKey != "" { + return pc.APIKey + } + } + if envVar := provider.EnvVarForProvider(providerName); envVar != "" { + return os.Getenv(envVar) + } + return "" +} + +func testProviderRequiresKey(providerName string) bool { + p := provider.GetProvider(provider.BaseProviderName(providerName)) + if p == nil { + return false + } + return p.RequiresAPIKey() +} + +func selectSmallestLocalModel() string { + models := whisper.ListModels() + if len(models) == 0 { + return "" + } + sort.Slice(models, func(i, j int) bool { + if models[i].SizeBytes == models[j].SizeBytes { + return !models[i].Multilingual && models[j].Multilingual + } + return models[i].SizeBytes < models[j].SizeBytes + }) + return models[0].ID +} + +func getModesForModel(model provider.Model) []string { + if model.SupportsBothModes() { + return []string{"batch", "streaming"} + } + if model.SupportsStreaming && !model.SupportsBatch { + return []string{"streaming"} + } + return []string{"batch"} +} + +func langDisplay(lang string) string { + if lang == "" { + return "auto" + } + return lang +} + +func truncateTestString(s string, max int) string { + if len(s) <= max { + return s + } + return s[:max] + "..." +} diff --git a/cmd/hyprvoice/main.go b/cmd/hyprvoice/main.go index 0582535..f4741ac 100644 --- a/cmd/hyprvoice/main.go +++ b/cmd/hyprvoice/main.go @@ -40,7 +40,6 @@ func init() { onboardingCmd(), configureCmd(), modelCmd(), - testModelsCmd(), ) } diff --git a/cmd/hyprvoice/test_models.go b/cmd/hyprvoice/test_models.go deleted file mode 100644 index 68b70f7..0000000 --- a/cmd/hyprvoice/test_models.go +++ /dev/null @@ -1,865 +0,0 @@ -package main - -import ( - "context" - "encoding/binary" - "encoding/json" - "errors" - "fmt" - "io" - "math" - "net/http" - "os" - "os/exec" - "path/filepath" - "sort" - "strings" - "time" - - "github.com/leonardotrapani/hyprvoice/internal/config" - "github.com/leonardotrapani/hyprvoice/internal/llm" - "github.com/leonardotrapani/hyprvoice/internal/models/whisper" - "github.com/leonardotrapani/hyprvoice/internal/provider" - "github.com/leonardotrapani/hyprvoice/internal/recording" - "github.com/leonardotrapani/hyprvoice/internal/transcriber" - "github.com/spf13/cobra" -) - -const ( - mockSampleRate = 16000 - mockChannels = 1 - mockBitsPerSample = 16 - defaultSampleURL = "https://raw.githubusercontent.com/mozilla/DeepSpeech/master/data/smoke_test/LDC93S1.wav" - defaultSampleName = "testaudio.wav" -) - -var ( - defaultTestKeywords = []string{"Hyprvoice", "transcription", "dictation"} - defaultTestLanguage = "en" -) - -type testModelsOptions struct { - audioPath string - recordFor time.Duration - timeout time.Duration - outputPath string - realtime bool - bothModes bool - localModel string - downloadLocal bool - language string - keywords []string - noKeywords bool - noLanguage bool -} - -type modelTest struct { - provider string - model provider.Model - mode string -} - -type modelTestResult struct { - Provider string `json:"provider"` - Model string `json:"model"` - Type string `json:"type"` - Mode string `json:"mode"` - Local bool `json:"local"` - Status string `json:"status"` - DurationMS int64 `json:"duration_ms"` - Output string `json:"output,omitempty"` - OutputChars int `json:"output_chars,omitempty"` - Error string `json:"error,omitempty"` -} - -type testReport struct { - StartedAt time.Time `json:"started_at"` - AudioSrc string `json:"audio_src"` - Results []modelTestResult `json:"results"` - PassCount int `json:"pass_count"` - FailCount int `json:"fail_count"` - SkipCount int `json:"skip_count"` - TotalCount int `json:"total_count"` -} - -func testModelsCmd() *cobra.Command { - var opts testModelsOptions - - cmd := &cobra.Command{ - Use: "test-models", - Short: "Run E2E tests for all providers/models", - RunE: func(cmd *cobra.Command, args []string) error { - return runTestModels(cmd.Context(), opts) - }, - } - - cmd.Flags().StringVar(&opts.audioPath, "audio", "", "WAV file to use (defaults to downloaded sample)") - cmd.Flags().DurationVar(&opts.recordFor, "record-seconds", 0, "Record mic audio (e.g. 5s)") - cmd.Flags().DurationVar(&opts.timeout, "timeout", 45*time.Second, "Per-model timeout") - cmd.Flags().BoolVar(&opts.realtime, "realtime", true, "Pace streaming chunks in real time") - cmd.Flags().BoolVar(&opts.bothModes, "both-modes", true, "Test batch+streaming models in both modes") - cmd.Flags().StringVar(&opts.outputPath, "output", "", "Write JSON report to file") - cmd.Flags().StringVar(&opts.localModel, "local-model", "", "whisper-cpp model ID to test") - cmd.Flags().BoolVar(&opts.downloadLocal, "download-local", false, "Download local whisper model if missing") - cmd.Flags().StringVar(&opts.language, "language", defaultTestLanguage, "Language code to test") - cmd.Flags().StringSliceVar(&opts.keywords, "keywords", defaultTestKeywords, "Keywords to test") - cmd.Flags().BoolVar(&opts.noKeywords, "no-keywords", false, "Skip keyword testing") - cmd.Flags().BoolVar(&opts.noLanguage, "no-language", false, "Skip language (use auto-detect)") - - return cmd -} - -func runTestModels(ctx context.Context, opts testModelsOptions) error { - if opts.audioPath != "" && opts.recordFor > 0 { - return fmt.Errorf("use either --audio or --record-seconds, not both") - } - if opts.timeout <= 0 { - return fmt.Errorf("timeout must be positive") - } - startedAt := time.Now().UTC() - - audio, audioSrc, err := loadTestAudio(ctx, opts) - if err != nil { - return err - } - - cfg, err := loadConfigForTests() - if err != nil { - return err - } - - transcriptionTests, err := buildTranscriptionTests(opts) - if err != nil { - return err - } - - llmTests := buildLLMTests() - - var results []modelTestResult - - for _, test := range transcriptionTests { - result := runTranscriptionTest(ctx, cfg, test, audio, opts) - results = append(results, result) - } - - for _, test := range llmTests { - result := runLLMTest(ctx, cfg, test, opts) - results = append(results, result) - } - - report := summarizeReport(startedAt, audioSrc, results) - printReport(report) - - if opts.outputPath != "" { - if err := writeReport(opts.outputPath, report); err != nil { - return err - } - } - - if report.FailCount > 0 || report.SkipCount > 0 { - return fmt.Errorf("%d failed, %d skipped", report.FailCount, report.SkipCount) - } - - return nil -} - -func loadConfigForTests() (*config.Config, error) { - cfg, err := config.Load() - if err != nil { - if errors.Is(err, config.ErrConfigNotFound) { - return config.DefaultConfig(), nil - } - return nil, err - } - if cfg.Providers == nil { - cfg.Providers = make(map[string]config.ProviderConfig) - } - return cfg, nil -} - -func buildTranscriptionTests(opts testModelsOptions) ([]modelTest, error) { - providerNames := provider.ListProvidersWithTranscription() - sort.Strings(providerNames) - - localModel := opts.localModel - if localModel == "" { - localModel = selectSmallestWhisperModel() - } - - var tests []modelTest - for _, providerName := range providerNames { - p := provider.GetProvider(providerName) - if p == nil { - continue - } - - models := provider.ModelsOfType(p, provider.Transcription) - sort.Slice(models, func(i, j int) bool { - return models[i].ID < models[j].ID - }) - - for _, model := range models { - if model.Local { - if providerName == provider.ProviderWhisperCpp && model.ID != localModel { - // test only the smallest local model; if it works the rest should too - continue - } - } - - if opts.bothModes && model.SupportsBothModes() { - tests = append(tests, modelTest{provider: providerName, model: model, mode: "batch"}) - tests = append(tests, modelTest{provider: providerName, model: model, mode: "streaming"}) - continue - } - - mode := "batch" - if model.SupportsStreaming && !model.SupportsBatch { - mode = "streaming" - } - tests = append(tests, modelTest{provider: providerName, model: model, mode: mode}) - } - } - - return tests, nil -} - -func buildLLMTests() []modelTest { - providerNames := provider.ListProvidersWithLLM() - sort.Strings(providerNames) - - var tests []modelTest - for _, providerName := range providerNames { - p := provider.GetProvider(providerName) - if p == nil { - continue - } - models := provider.ModelsOfType(p, provider.LLM) - sort.Slice(models, func(i, j int) bool { - return models[i].ID < models[j].ID - }) - for _, model := range models { - tests = append(tests, modelTest{provider: providerName, model: model, mode: "batch"}) - } - } - - return tests -} - -func runTranscriptionTest(ctx context.Context, cfg *config.Config, test modelTest, audio []byte, opts testModelsOptions) modelTestResult { - result := modelTestResult{ - Provider: test.provider, - Model: test.model.ID, - Type: "transcription", - Mode: test.mode, - Local: test.model.Local, - Status: "fail", - } - - if test.model.Local { - if _, err := exec.LookPath("whisper-cli"); err != nil { - result.Status = "skip" - result.Error = "whisper-cli not found" - return result - } - if !whisper.IsInstalled(test.model.ID) { - if opts.downloadLocal { - dlCtx, cancel := context.WithTimeout(ctx, 5*time.Minute) - defer cancel() - if err := downloadLocalModel(dlCtx, test.model.ID); err != nil { - result.Status = "fail" - result.Error = err.Error() - return result - } - } else { - result.Status = "skip" - result.Error = "local model not installed" - return result - } - } - } - - apiKey := resolveAPIKey(cfg, test.provider) - if providerRequiresKey(test.provider) && apiKey == "" { - result.Status = "skip" - result.Error = "missing api key" - return result - } - - language := opts.language - if opts.noLanguage { - language = "" - } - keywords := opts.keywords - if opts.noKeywords { - keywords = nil - } - - streaming := test.mode == "streaming" - transcribeCfg := transcriber.Config{ - Provider: test.provider, - APIKey: apiKey, - Language: language, - Model: test.model.ID, - Keywords: keywords, - Threads: 0, - Streaming: streaming, - } - - testCtx, cancel := context.WithTimeout(ctx, opts.timeout) - defer cancel() - start := time.Now() - text, err := runTranscriber(testCtx, transcribeCfg, audio, opts.realtime) - result.DurationMS = time.Since(start).Milliseconds() - if err != nil { - result.Error = err.Error() - return result - } - - result.Status = "pass" - result.Output = strings.TrimSpace(text) - result.OutputChars = len(result.Output) - return result -} - -func runLLMTest(ctx context.Context, cfg *config.Config, test modelTest, opts testModelsOptions) modelTestResult { - result := modelTestResult{ - Provider: test.provider, - Model: test.model.ID, - Type: "llm", - Mode: "batch", - Local: test.model.Local, - Status: "fail", - } - - apiKey := resolveAPIKey(cfg, test.provider) - if providerRequiresKey(test.provider) && apiKey == "" { - result.Status = "skip" - result.Error = "missing api key" - return result - } - - keywords := opts.keywords - if opts.noKeywords { - keywords = nil - } - - llmCfg := llm.Config{ - Provider: test.provider, - APIKey: apiKey, - Model: test.model.ID, - RemoveStutters: true, - AddPunctuation: true, - FixGrammar: true, - RemoveFillerWords: true, - CustomPrompt: "", - Keywords: keywords, - } - - adapter, err := llm.NewAdapter(llmCfg) - if err != nil { - result.Error = err.Error() - return result - } - - input := "uh i i i want to test hyprvoice you know this is just a cleanup check" - testCtx, cancel := context.WithTimeout(ctx, opts.timeout) - defer cancel() - start := time.Now() - output, err := adapter.Process(testCtx, input) - result.DurationMS = time.Since(start).Milliseconds() - if err != nil { - result.Error = err.Error() - return result - } - - result.Status = "pass" - result.Output = strings.TrimSpace(output) - result.OutputChars = len(result.Output) - return result -} - -func runTranscriber(ctx context.Context, cfg transcriber.Config, audio []byte, realtime bool) (string, error) { - t, err := transcriber.NewTranscriber(cfg) - if err != nil { - return "", err - } - - frameCh := make(chan recording.AudioFrame, 8) - errCh, err := t.Start(ctx, frameCh) - if err != nil { - return "", err - } - - sendErr := sendAudioFrames(ctx, frameCh, audio, realtime) - close(frameCh) - - stopErr := t.Stop(ctx) - errChErr := readErrorChannel(errCh) - - if sendErr != nil { - return "", sendErr - } - if stopErr != nil { - return "", stopErr - } - if errChErr != nil { - return "", errChErr - } - - return t.GetFinalTranscription() -} - -func sendAudioFrames(ctx context.Context, frameCh chan<- recording.AudioFrame, audio []byte, realtime bool) error { - const chunkBytes = 3200 - bytesPerSecond := mockSampleRate * (mockBitsPerSample / 8) * mockChannels - chunkDuration := time.Duration(float64(chunkBytes) / float64(bytesPerSecond) * float64(time.Second)) - - for offset := 0; offset < len(audio); offset += chunkBytes { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - - end := offset + chunkBytes - if end > len(audio) { - end = len(audio) - } - - frame := recording.AudioFrame{Data: audio[offset:end], Timestamp: time.Now()} - select { - case frameCh <- frame: - case <-ctx.Done(): - return ctx.Err() - } - - if realtime { - time.Sleep(chunkDuration) - } - } - - return nil -} - -func readErrorChannel(errCh <-chan error) error { - var firstErr error - if errCh == nil { - return nil - } - - idleTimer := time.NewTimer(150 * time.Millisecond) - defer idleTimer.Stop() - - for { - select { - case err, ok := <-errCh: - if !ok { - return firstErr - } - if err != nil && firstErr == nil { - firstErr = err - } - if !idleTimer.Stop() { - <-idleTimer.C - } - idleTimer.Reset(150 * time.Millisecond) - case <-idleTimer.C: - return firstErr - } - } -} - -func loadTestAudio(ctx context.Context, opts testModelsOptions) ([]byte, string, error) { - if opts.audioPath != "" { - wav, err := readWAVFile(opts.audioPath) - if err != nil { - return nil, "", err - } - return wav.data, opts.audioPath, nil - } - - if opts.recordFor > 0 { - audio, err := recordAudio(ctx, opts.recordFor) - if err != nil { - return nil, "", err - } - return audio, fmt.Sprintf("recording:%s", opts.recordFor), nil - } - - path, err := ensureDefaultSample(ctx) - if err != nil { - return nil, "", err - } - wav, err := readWAVFile(path) - if err != nil { - return nil, "", err - } - return wav.data, path, nil -} - -type wavData struct { - data []byte - sampleRate int - channels int - bitsPerSample int -} - -func readWAVFile(path string) (*wavData, error) { - data, err := os.ReadFile(path) - if err != nil { - return nil, err - } - return parseWAV(data) -} - -func parseWAV(data []byte) (*wavData, error) { - if len(data) < 12 { - return nil, fmt.Errorf("invalid wav: too short") - } - if string(data[0:4]) != "RIFF" || string(data[8:12]) != "WAVE" { - return nil, fmt.Errorf("invalid wav: missing riff/wave header") - } - - offset := 12 - var fmtFound bool - var dataFound bool - var info wavData - - for offset+8 <= len(data) { - chunkID := string(data[offset : offset+4]) - chunkSize := int(binary.LittleEndian.Uint32(data[offset+4 : offset+8])) - offset += 8 - if offset+chunkSize > len(data) { - return nil, fmt.Errorf("invalid wav: chunk overflows file") - } - - switch chunkID { - case "fmt ": - if chunkSize < 16 { - return nil, fmt.Errorf("invalid wav: fmt chunk too short") - } - audioFormat := binary.LittleEndian.Uint16(data[offset : offset+2]) - if audioFormat != 1 { - return nil, fmt.Errorf("unsupported wav format: %d", audioFormat) - } - info.channels = int(binary.LittleEndian.Uint16(data[offset+2 : offset+4])) - info.sampleRate = int(binary.LittleEndian.Uint32(data[offset+4 : offset+8])) - info.bitsPerSample = int(binary.LittleEndian.Uint16(data[offset+14 : offset+16])) - fmtFound = true - case "data": - info.data = data[offset : offset+chunkSize] - dataFound = true - } - - offset += chunkSize - if chunkSize%2 == 1 { - offset++ - } - } - - if !fmtFound || !dataFound { - return nil, fmt.Errorf("invalid wav: missing fmt or data chunk") - } - if info.bitsPerSample != mockBitsPerSample { - return nil, fmt.Errorf("unsupported wav bits per sample: %d", info.bitsPerSample) - } - if info.sampleRate <= 0 { - return nil, fmt.Errorf("invalid wav sample rate: %d", info.sampleRate) - } - if info.channels <= 0 { - return nil, fmt.Errorf("invalid wav: channels=%d", info.channels) - } - if len(info.data)%2 != 0 { - return nil, fmt.Errorf("invalid wav: pcm data not aligned") - } - - monoData, err := downmixToMono(info.data, info.channels) - if err != nil { - return nil, err - } - resampled := resamplePCM16(monoData, info.sampleRate, mockSampleRate) - if len(resampled) == 0 { - return nil, fmt.Errorf("invalid wav: empty audio data") - } - info.data = resampled - info.sampleRate = mockSampleRate - info.channels = mockChannels - info.bitsPerSample = mockBitsPerSample - return &info, nil -} - -func downmixToMono(data []byte, channels int) ([]byte, error) { - if channels == 1 { - return data, nil - } - if channels <= 0 { - return nil, fmt.Errorf("invalid channel count: %d", channels) - } - frameSize := 2 * channels - if len(data)%frameSize != 0 { - return nil, fmt.Errorf("invalid pcm data length") - } - - frames := len(data) / frameSize - out := make([]byte, frames*2) - for i := 0; i < frames; i++ { - var sum int32 - for c := 0; c < channels; c++ { - idx := (i*channels + c) * 2 - sample := int16(binary.LittleEndian.Uint16(data[idx : idx+2])) - sum += int32(sample) - } - mono := int16(sum / int32(channels)) - out[i*2] = byte(mono) - out[i*2+1] = byte(mono >> 8) - } - - return out, nil -} - -func resamplePCM16(data []byte, inRate, outRate int) []byte { - if inRate <= 0 || outRate <= 0 { - return data - } - if inRate == outRate { - return data - } - if len(data) < 2 { - return data - } - - numInSamples := len(data) / 2 - numOutSamples := int(math.Round(float64(numInSamples) * float64(outRate) / float64(inRate))) - if numOutSamples <= 0 { - return nil - } - - out := make([]byte, numOutSamples*2) - for i := 0; i < numOutSamples; i++ { - srcPos := float64(i) * float64(inRate) / float64(outRate) - srcIdx := int(srcPos) - frac := srcPos - float64(srcIdx) - - sample1 := sampleAtPCM16(data, srcIdx) - sample2 := sampleAtPCM16(data, srcIdx+1) - outSample := int16(float64(sample1)*(1-frac) + float64(sample2)*frac) - - out[i*2] = byte(outSample) - out[i*2+1] = byte(outSample >> 8) - } - - return out -} - -func sampleAtPCM16(data []byte, idx int) int16 { - if idx <= 0 { - return int16(binary.LittleEndian.Uint16(data[0:2])) - } - pos := idx * 2 - if pos+1 >= len(data) { - last := len(data) - 2 - if last < 0 { - return 0 - } - return int16(binary.LittleEndian.Uint16(data[last : last+2])) - } - return int16(binary.LittleEndian.Uint16(data[pos : pos+2])) -} - -func recordAudio(ctx context.Context, duration time.Duration) ([]byte, error) { - recorder := recording.NewRecorder(recording.Config{ - SampleRate: mockSampleRate, - Channels: mockChannels, - Format: "s16", - BufferSize: 8192, - Device: "", - ChannelBufferSize: 30, - Timeout: duration + 2*time.Second, - }) - - frameCh, errCh, err := recorder.Start(ctx) - if err != nil { - return nil, err - } - - var audio []byte - stopCh := make(chan struct{}) - go func() { - for frame := range frameCh { - audio = append(audio, frame.Data...) - } - close(stopCh) - }() - - select { - case <-time.After(duration): - recorder.Stop() - case <-ctx.Done(): - recorder.Stop() - } - - <-stopCh - if err := readErrorChannel(errCh); err != nil { - return nil, err - } - - return audio, nil -} - -func ensureDefaultSample(ctx context.Context) (string, error) { - cacheDir, err := os.UserCacheDir() - if err != nil { - return "", err - } - path := filepath.Join(cacheDir, "hyprvoice", defaultSampleName) - if info, err := os.Stat(path); err == nil && info.Size() > 0 { - return path, nil - } - if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { - return "", err - } - - fmt.Printf("test-models: downloading sample audio...\n") - if err := downloadSample(ctx, defaultSampleURL, path); err != nil { - return "", fmt.Errorf("download sample: %w (use --audio or --record-seconds to skip download)", err) - } - return path, nil -} - -func downloadSample(ctx context.Context, url, path string) error { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return err - } - resp, err := http.DefaultClient.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("download failed: %s", resp.Status) - } - - tmpPath := path + ".downloading" - out, err := os.Create(tmpPath) - if err != nil { - return err - } - defer func() { - out.Close() - _ = os.Remove(tmpPath) - }() - - if _, err := io.Copy(out, resp.Body); err != nil { - return err - } - if err := out.Close(); err != nil { - return err - } - return os.Rename(tmpPath, path) -} - -func resolveAPIKey(cfg *config.Config, providerName string) string { - base := provider.BaseProviderName(providerName) - if cfg != nil && cfg.Providers != nil { - if pc, ok := cfg.Providers[base]; ok && pc.APIKey != "" { - return pc.APIKey - } - } - if envVar := provider.EnvVarForProvider(providerName); envVar != "" { - return os.Getenv(envVar) - } - return "" -} - -func providerRequiresKey(providerName string) bool { - p := provider.GetProvider(provider.BaseProviderName(providerName)) - if p == nil { - return false - } - return p.RequiresAPIKey() -} - -func selectSmallestWhisperModel() string { - models := whisper.ListModels() - if len(models) == 0 { - return "" - } - sort.Slice(models, func(i, j int) bool { - if models[i].SizeBytes == models[j].SizeBytes { - return !models[i].Multilingual && models[j].Multilingual - } - return models[i].SizeBytes < models[j].SizeBytes - }) - return models[0].ID -} - -func downloadLocalModel(ctx context.Context, modelID string) error { - var lastPercent int64 - return whisper.Download(ctx, modelID, func(downloaded, total int64) { - if total <= 0 { - return - } - percent := downloaded * 100 / total - if percent >= lastPercent+10 { - fmt.Printf("downloading %s... %d%%\n", modelID, percent) - lastPercent = percent - } - }) -} - -func summarizeReport(startedAt time.Time, audioSrc string, results []modelTestResult) testReport { - report := testReport{ - StartedAt: startedAt, - AudioSrc: audioSrc, - Results: results, - } - for _, r := range results { - report.TotalCount++ - switch r.Status { - case "pass": - report.PassCount++ - case "fail": - report.FailCount++ - case "skip": - report.SkipCount++ - } - } - return report -} - -func printReport(report testReport) { - fmt.Printf("test-models: total=%d pass=%d fail=%d skip=%d\n", report.TotalCount, report.PassCount, report.FailCount, report.SkipCount) - fmt.Printf("audio: %s\n", report.AudioSrc) - for _, r := range report.Results { - line := fmt.Sprintf("%s %s/%s %s", r.Status, r.Provider, r.Model, r.Mode) - if r.Type == "llm" { - line = fmt.Sprintf("%s %s/%s llm", r.Status, r.Provider, r.Model) - } - if r.DurationMS > 0 { - line += fmt.Sprintf(" %dms", r.DurationMS) - } - if r.Error != "" { - line += fmt.Sprintf(" error=%s", truncateString(r.Error, 160)) - } - if r.Output != "" { - line += fmt.Sprintf(" output=%q", truncateString(r.Output, 120)) - } - fmt.Println(line) - } -} - -func writeReport(path string, report testReport) error { - data, err := json.MarshalIndent(report, "", " ") - if err != nil { - return err - } - return os.WriteFile(path, data, 0600) -} - -func truncateString(s string, max int) string { - if len(s) <= max { - return s - } - return s[:max] + "..." -} diff --git a/testdata/sample.wav b/testdata/sample.wav new file mode 100644 index 0000000000000000000000000000000000000000..62b65f8fc67dab9e3b46c5395daebf74654c9ce5 GIT binary patch literal 93638 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 DejC}J literal 0 HcmV?d00001