Ariver
2026-06-30 ac31349b32d89b2798cd20224fcdede91d6bfe05
privatevoice.src/internal/engine/engine.go
@@ -2,9 +2,12 @@
import (
   "fmt"
   "time"
   "voicesnap/internal/config"
   "voicesnap/internal/language"
   "voicesnap/internal/logger"
   "voicesnap/internal/model"
   "voicesnap/internal/modelselection"
   "voicesnap/internal/paths"
)
@@ -15,6 +18,33 @@
   // HardwareInfo returns a human-readable description of the hardware backend being used.
   HardwareInfo() string
   // Close releases engine resources.
   Close()
}
// StreamingEngine is implemented by engines that can expose partial results
// while audio is still being captured.
type StreamingEngine interface {
   NewStreamingSession() (StreamingSession, error)
}
// ReleaseTailCaptureEngine is implemented by engines that need a short audio
// capture grace period after the user releases the recording hotkey.
type ReleaseTailCaptureEngine interface {
   ReleaseTailCaptureDelay() time.Duration
}
// HoldPreCaptureEngine is implemented by engines that should start capturing
// audio immediately on hold-to-talk key down, before the activation delay has
// confirmed that the hotkey was not part of a key combination.
type HoldPreCaptureEngine interface {
   HoldPreCaptureEnabled() bool
}
// StreamingSession receives incremental 16kHz mono PCM and returns the current
// best transcript for the active utterance.
type StreamingSession interface {
   Accept(samples []float32) (string, error)
   Finish() (string, error)
   Close()
}
@@ -60,9 +90,10 @@
   cfg, err := config.Load()
   if err != nil {
      logger.Error("Failed to load config for model selection: %v", err)
      return NewWithModelID(model.DefaultModelID)
      cfg = config.Default()
   }
   return NewWithModelID(cfg.SelectedModelID)
   current := modelselection.Resolve(cfg, language.NewSystemDetector())
   return NewWithModelID(current.ModelID)
}
func NewWithModelID(modelID string) (Engine, error) {
@@ -77,18 +108,28 @@
   if !resolved.IsUsable() {
      return nil, fmt.Errorf("model %s is not usable: %s missing=%v problems=%v", resolved.ModelID, resolved.Status, resolved.Missing, resolved.Problems)
   }
   if resolved.BackendKind != model.BackendSenseVoice {
   if !isSupportedBackend(resolved.BackendKind) {
      return nil, fmt.Errorf("unsupported backend kind: %s", resolved.BackendKind)
   }
   logger.Info("Loading model %s from %s", resolved.ModelID, resolved.RootDir)
   logger.Info("Loading model %s backend=%s from %s", resolved.ModelID, resolved.BackendKind, resolved.RootDir)
   return newPlatformEngine(resolved)
}
func isSupportedBackend(backend string) bool {
   switch backend {
   case model.BackendSenseVoice, model.BackendMoonshine, model.BackendTransducer, model.BackendNemoTransducer, model.BackendQwen3ASR, model.BackendXASRStreaming:
      return true
   default:
      return false
   }
}
func resolveCurrentModel() (model.ResolvedModel, error) {
   cfg, err := config.Load()
   if err != nil {
      return model.ResolveModel(model.DefaultModelID)
      cfg = config.Default()
   }
   return model.ResolveModel(model.NormalizeModelID(cfg.SelectedModelID))
   current := modelselection.Resolve(cfg, language.NewSystemDetector())
   return model.ResolveModel(current.ModelID)
}