refactor(go bindings): fix backward compatibility for the interfaces

This commit is contained in:
ciricc 2025-09-14 02:31:10 +03:00
parent ba990ab0a8
commit 221e93a5d7
5 changed files with 156 additions and 211 deletions

View File

@ -12,19 +12,17 @@ import (
)
type context struct {
n int
model *model
st WhisperState
params *parameters
Parameters
n int
model *model
st WhisperState
*Parameters
}
func newContext(model *model, params *parameters) (Context, error) {
func NewContext(model *model, params *Parameters) (*context, error) {
c := new(context)
c.model = model
c.params = params
c.Parameters = c.params
c.Parameters = params
// allocate isolated state per context
ctx, err := model.whisperContext().unsafeContext()
@ -68,8 +66,8 @@ func (context *context) Close() error {
}
// Params returns a high-level parameters wrapper
func (context *context) Params() Parameters {
return context.params
func (context *context) Params() *Parameters {
return context.Parameters
}
// ResetTimings resets the model performance timing counters.
@ -87,7 +85,7 @@ func (context *context) PrintTimings() {
// SystemInfo returns the system information
func (context *context) SystemInfo() string {
return fmt.Sprintf("system_info: n_threads = %d / %d | %s\n",
context.params.Threads(),
context.Parameters.Threads(),
runtime.NumCPU(),
whisper.Whisper_print_system_info(),
)
@ -129,10 +127,10 @@ func (context *context) Process(
// If the callback is defined then we force on single_segment mode
if callNewSegment != nil {
context.params.SetSingleSegment(true)
context.Parameters.SetSingleSegment(true)
}
lowLevelParams, err := context.params.unsafeParams()
lowLevelParams, err := context.Parameters.unsafeParams()
if err != nil {
return err
}
@ -242,7 +240,7 @@ func (context *context) SetLanguage(lang string) error {
return ErrModelNotMultilingual
}
return context.params.SetLanguage(lang)
return context.Parameters.SetLanguage(lang)
}
// Deprecated: Use Model.IsLANG() instead - token checking is model-specific.

View File

@ -325,11 +325,11 @@ func TestContext_VAD_And_Diarization_Params_DoNotPanic(t *testing.T) {
assert.Equal(uint16(1), dec.NumChans)
data := buf.AsFloat32Buffer().Data
model, err := whisper.New(ModelPath)
model, err := whisper.NewModel(ModelPath)
assert.NoError(err)
defer func() { _ = model.Close() }()
ctx, err := model.NewContext()
ctx, err := whisper.NewContext(model, nil)
assert.NoError(err)
defer func() { _ = ctx.Close() }()
@ -358,12 +358,11 @@ func TestDiarization_TwoSpeakers_Boundaries(t *testing.T) {
require.NoError(t, err)
data := buf.AsFloat32Buffer().Data
model, err := whisper.New(ModelTinydiarizePath)
model, err := whisper.NewModel(ModelTinydiarizePath)
require.NoError(t, err)
defer func() { _ = model.Close() }()
// diarize ON with beam search and tighter segmentation
ctxOn, err := model.NewContextWithParams(whisper.SAMPLING_GREEDY, func(p whisper.Parameters) {
params, err := whisper.NewParameters(model, whisper.SAMPLING_GREEDY, func(p *whisper.Parameters) {
p.SetDiarize(true)
p.SetVAD(false)
p.SetSplitOnWord(true)
@ -372,6 +371,10 @@ func TestDiarization_TwoSpeakers_Boundaries(t *testing.T) {
p.SetTokenTimestamps(true)
})
require.NoError(t, err)
// diarize ON with beam search and tighter segmentation
ctxOn, err := whisper.NewContext(model, params)
require.NoError(t, err)
defer func() { _ = ctxOn.Close() }()
require.NoError(t, ctxOn.Process(data, nil, nil, nil))
@ -389,15 +392,7 @@ func TestDiarization_TwoSpeakers_Boundaries(t *testing.T) {
require.Greater(t, turnsOn, 0, "expected speaker turn boundaries with diarization enabled")
// diarize OFF baseline with same segmentation and beam
ctxOff, err := model.NewContextWithParams(whisper.SAMPLING_BEAM_SEARCH, func(p whisper.Parameters) {
p.SetBeamSize(3)
p.SetDiarize(false)
p.SetVAD(false)
p.SetSplitOnWord(true)
p.SetMaxSegmentLength(40)
p.SetMaxTokensPerSegment(64)
p.SetTokenTimestamps(true)
})
ctxOff, err := whisper.NewContext(model, params)
require.NoError(t, err)
defer func() { _ = ctxOff.Close() }()

View File

@ -46,34 +46,19 @@ type TokenIdentifier interface {
IsText(Token) (bool, error)
}
type ParamsConfigure func(Parameters)
type ParamsConfigure func(*Parameters)
// Model is the interface to a whisper model. Create a new model with the
// function whisper.New(string)
// Deprecated: Use NewModel implementation struct instead of relying on this interface
type Model interface {
io.Closer
// Return a new speech-to-text context.
// It may return an error is the model is not loaded or closed
// Deprecated: Use NewContext implementation struct instead of relying on this interface
NewContext() (Context, error)
// Return a new parameters wrapper
// sampling is the sampling strategy to use
// configure is the function to configure the parameters
// It may return an error is the model is not loaded or closed
NewParams(
sampling SamplingStrategy,
configure ParamsConfigure,
) (Parameters, error)
// Return a new speech-to-text context configured via the provided function
// and sampling strategy. The context is backed by an isolated whisper_state.
// It may return an error is the model is not loaded or closed
NewContextWithParams(
sampling SamplingStrategy,
configure ParamsConfigure,
) (Context, error)
// Return true if the model is multilingual.
// It returns false if the model is not loaded or closed
IsMultilingual() bool
@ -87,73 +72,65 @@ type Model interface {
// Reset model performance timing counters
ResetTimings()
// WhisperContext returns the memory-safe whisper context wrapper of the raw whisper context
// You may need to use this to get the raw whisper context
// Ot check that the model's context is not closed
WhisperContext() WhisperContext
// Token identifier
TokenIdentifier() TokenIdentifier
}
// Parameters configures decode / processing behavior
type Parameters interface {
SetTranslate(bool)
SetSplitOnWord(bool)
SetThreads(uint)
SetOffset(time.Duration)
SetDuration(time.Duration)
SetTokenThreshold(float32)
SetTokenSumThreshold(float32)
SetMaxSegmentLength(uint)
SetTokenTimestamps(bool)
SetMaxTokensPerSegment(uint)
SetAudioCtx(uint)
SetMaxContext(n int)
SetBeamSize(n int)
SetEntropyThold(t float32)
SetInitialPrompt(prompt string)
// // Parameters configures decode / processing behavior
// type Parameters interface {
// SetTranslate(bool)
// SetSplitOnWord(bool)
// SetThreads(uint)
// SetOffset(time.Duration)
// SetDuration(time.Duration)
// SetTokenThreshold(float32)
// SetTokenSumThreshold(float32)
// SetMaxSegmentLength(uint)
// SetTokenTimestamps(bool)
// SetMaxTokensPerSegment(uint)
// SetAudioCtx(uint)
// SetMaxContext(n int)
// SetBeamSize(n int)
// SetEntropyThold(t float32)
// SetInitialPrompt(prompt string)
SetNoContext(bool)
SetPrintSpecial(bool)
SetPrintProgress(bool)
SetPrintRealtime(bool)
SetPrintTimestamps(bool)
// SetNoContext(bool)
// SetPrintSpecial(bool)
// SetPrintProgress(bool)
// SetPrintRealtime(bool)
// SetPrintTimestamps(bool)
// Enable extra debug info (e.g., dump log_mel)
SetDebugMode(bool)
// Diarization (tinydiarize)
SetDiarize(bool)
// // Enable extra debug info (e.g., dump log_mel)
// SetDebugMode(bool)
// // Diarization (tinydiarize)
// SetDiarize(bool)
// Voice Activity Detection (VAD)
SetVAD(bool)
SetVADModelPath(string)
SetVADThreshold(float32)
SetVADMinSpeechMs(int)
SetVADMinSilenceMs(int)
SetVADMaxSpeechSec(float32)
SetVADSpeechPadMs(int)
SetVADSamplesOverlap(float32)
// // Voice Activity Detection (VAD)
// SetVAD(bool)
// SetVADModelPath(string)
// SetVADThreshold(float32)
// SetVADMinSpeechMs(int)
// SetVADMinSilenceMs(int)
// SetVADMaxSpeechSec(float32)
// SetVADSpeechPadMs(int)
// SetVADSamplesOverlap(float32)
// Set the temperature
SetTemperature(t float32)
// // Set the temperature
// SetTemperature(t float32)
// Set the fallback temperature incrementation
// Pass -1.0 to disable this feature
SetTemperatureFallback(t float32)
// // Set the fallback temperature incrementation
// // Pass -1.0 to disable this feature
// SetTemperatureFallback(t float32)
// Set the language
// If the model is not multilingual, this will return an error
SetLanguage(string) error
// // Set the language
// // If the model is not multilingual, this will return an error
// SetLanguage(string) error
// Set single segment mode
SetSingleSegment(bool)
// // Set single segment mode
// SetSingleSegment(bool)
// Getter methods
Language() string
Threads() int
}
// // Getter methods
// Language() string
// Threads() int
// }
// Context is the speech recognition context.
type Context interface {
@ -217,9 +194,6 @@ type Context interface {
// Deprecated: Use Params().Language() instead
Language() string
// Return the model that the context is backed by
Model() Model
// Deprecated: Use Model().IsMultilingual() instead
IsMultilingual() bool
@ -269,9 +243,6 @@ type Context interface {
// SystemInfo returns the system information
SystemInfo() string
// Params returns a high-level parameters wrapper - preferred method
Params() Parameters
}
// Segment is the text result of a speech recognition.

View File

@ -17,7 +17,15 @@ type model struct {
// Make sure model adheres to the interface
var _ Model = (*model)(nil)
// Deprecated: Use NewModel instead
func New(path string) (Model, error) {
return NewModel(path)
}
// NewModel creates a new model without initializing the context
func NewModel(
path string,
) (*model, error) {
model := new(model)
if _, err := os.Stat(path); err != nil {
return nil, err
@ -92,73 +100,18 @@ func (model *model) Languages() []string {
// Each context is backed by an isolated whisper_state for safe concurrent processing.
func (model *model) NewContext() (Context, error) {
// Create new context with default params
params, err := model.newParams(SAMPLING_GREEDY, nil)
params, err := NewParameters(model, SAMPLING_GREEDY, nil)
if err != nil {
return nil, err
}
// Return new context (now state-backed)
return newContext(
return NewContext(
model,
params,
)
}
func (model *model) NewParams(
sampling SamplingStrategy,
configure ParamsConfigure,
) (Parameters, error) {
return model.newParams(sampling, nil)
}
// NewContextWithParams creates a new speech-to-text context and allows
// callers to customize the decoding parameters before the state is used.
// The resulting Context is backed by an isolated whisper_state for safe
// concurrent processing.
func (model *model) NewContextWithParams(
sampling SamplingStrategy,
configure ParamsConfigure,
) (Context, error) {
params, err := model.newParams(sampling, configure)
if err != nil {
return nil, err
}
return newContext(
model,
params,
)
}
func defaultParamsConfigure(params Parameters) {
params.SetTranslate(false)
params.SetPrintSpecial(false)
params.SetPrintProgress(false)
params.SetPrintRealtime(false)
params.SetPrintTimestamps(false)
}
func (m *model) newParams(
sampling SamplingStrategy,
configure ParamsConfigure,
) (*parameters, error) {
ctx, err := m.ctx.unsafeContext()
if err != nil {
return nil, ErrModelClosed
}
p := ctx.Whisper_full_default_params(whisper.SamplingStrategy(sampling))
safeParams := newParameters(&p)
defaultParamsConfigure(safeParams)
if configure != nil {
configure(safeParams)
}
return safeParams, nil
}
// PrintTimings prints the model performance timings to stdout.
func (model *model) PrintTimings() {
ctx, err := model.ctx.unsafeContext()

View File

@ -1,62 +1,92 @@
package whisper
import (
"runtime"
"time"
// Bindings
whisper "github.com/ggerganov/whisper.cpp/bindings/go"
)
// parameters is a high-level wrapper that implements the Parameters interface
// Parameters is a high-level wrapper that implements the Parameters interface
// and delegates to the underlying low-level whisper.Params.
type parameters struct {
type Parameters struct {
p *whisper.Params
}
func newParameters(whisperParams *whisper.Params) *parameters {
return &parameters{
p: whisperParams,
}
func defaultParamsConfigure(params *Parameters) {
params.SetTranslate(false)
params.SetPrintSpecial(false)
params.SetPrintProgress(false)
params.SetPrintRealtime(false)
params.SetPrintTimestamps(false)
// Default behavior backward compatibility
params.SetThreads(uint(runtime.NumCPU()))
params.SetNoContext(true)
}
func (w *parameters) SetTranslate(v bool) { w.p.SetTranslate(v) }
func (w *parameters) SetSplitOnWord(v bool) { w.p.SetSplitOnWord(v) }
func (w *parameters) SetThreads(v uint) { w.p.SetThreads(int(v)) }
func (w *parameters) SetOffset(d time.Duration) { w.p.SetOffset(int(d.Milliseconds())) }
func (w *parameters) SetDuration(d time.Duration) { w.p.SetDuration(int(d.Milliseconds())) }
func (w *parameters) SetTokenThreshold(t float32) { w.p.SetTokenThreshold(t) }
func (w *parameters) SetTokenSumThreshold(t float32) { w.p.SetTokenSumThreshold(t) }
func (w *parameters) SetMaxSegmentLength(n uint) { w.p.SetMaxSegmentLength(int(n)) }
func (w *parameters) SetTokenTimestamps(b bool) { w.p.SetTokenTimestamps(b) }
func (w *parameters) SetMaxTokensPerSegment(n uint) { w.p.SetMaxTokensPerSegment(int(n)) }
func (w *parameters) SetAudioCtx(n uint) { w.p.SetAudioCtx(int(n)) }
func (w *parameters) SetMaxContext(n int) { w.p.SetMaxContext(n) }
func (w *parameters) SetBeamSize(n int) { w.p.SetBeamSize(n) }
func (w *parameters) SetEntropyThold(t float32) { w.p.SetEntropyThold(t) }
func (w *parameters) SetInitialPrompt(prompt string) { w.p.SetInitialPrompt(prompt) }
func (w *parameters) SetTemperature(t float32) { w.p.SetTemperature(t) }
func (w *parameters) SetTemperatureFallback(t float32) { w.p.SetTemperatureFallback(t) }
func (w *parameters) SetNoContext(v bool) { w.p.SetNoContext(v) }
func (w *parameters) SetPrintSpecial(v bool) { w.p.SetPrintSpecial(v) }
func (w *parameters) SetPrintProgress(v bool) { w.p.SetPrintProgress(v) }
func (w *parameters) SetPrintRealtime(v bool) { w.p.SetPrintRealtime(v) }
func (w *parameters) SetPrintTimestamps(v bool) { w.p.SetPrintTimestamps(v) }
func (w *parameters) SetDebugMode(v bool) { w.p.SetDebugMode(v) }
func NewParameters(
model *model,
sampling SamplingStrategy,
configure ParamsConfigure,
) (*Parameters, error) {
ctx, err := model.ctx.unsafeContext()
if err != nil {
return nil, ErrModelClosed
}
p := ctx.Whisper_full_default_params(whisper.SamplingStrategy(sampling))
safeParams := &Parameters{
p: &p,
}
defaultParamsConfigure(safeParams)
if configure != nil {
configure(safeParams)
}
return safeParams, nil
}
func (w *Parameters) SetTranslate(v bool) { w.p.SetTranslate(v) }
func (w *Parameters) SetSplitOnWord(v bool) { w.p.SetSplitOnWord(v) }
func (w *Parameters) SetThreads(v uint) { w.p.SetThreads(int(v)) }
func (w *Parameters) SetOffset(d time.Duration) { w.p.SetOffset(int(d.Milliseconds())) }
func (w *Parameters) SetDuration(d time.Duration) { w.p.SetDuration(int(d.Milliseconds())) }
func (w *Parameters) SetTokenThreshold(t float32) { w.p.SetTokenThreshold(t) }
func (w *Parameters) SetTokenSumThreshold(t float32) { w.p.SetTokenSumThreshold(t) }
func (w *Parameters) SetMaxSegmentLength(n uint) { w.p.SetMaxSegmentLength(int(n)) }
func (w *Parameters) SetTokenTimestamps(b bool) { w.p.SetTokenTimestamps(b) }
func (w *Parameters) SetMaxTokensPerSegment(n uint) { w.p.SetMaxTokensPerSegment(int(n)) }
func (w *Parameters) SetAudioCtx(n uint) { w.p.SetAudioCtx(int(n)) }
func (w *Parameters) SetMaxContext(n int) { w.p.SetMaxContext(n) }
func (w *Parameters) SetBeamSize(n int) { w.p.SetBeamSize(n) }
func (w *Parameters) SetEntropyThold(t float32) { w.p.SetEntropyThold(t) }
func (w *Parameters) SetInitialPrompt(prompt string) { w.p.SetInitialPrompt(prompt) }
func (w *Parameters) SetTemperature(t float32) { w.p.SetTemperature(t) }
func (w *Parameters) SetTemperatureFallback(t float32) { w.p.SetTemperatureFallback(t) }
func (w *Parameters) SetNoContext(v bool) { w.p.SetNoContext(v) }
func (w *Parameters) SetPrintSpecial(v bool) { w.p.SetPrintSpecial(v) }
func (w *Parameters) SetPrintProgress(v bool) { w.p.SetPrintProgress(v) }
func (w *Parameters) SetPrintRealtime(v bool) { w.p.SetPrintRealtime(v) }
func (w *Parameters) SetPrintTimestamps(v bool) { w.p.SetPrintTimestamps(v) }
func (w *Parameters) SetDebugMode(v bool) { w.p.SetDebugMode(v) }
// Diarization (tinydiarize)
func (w *parameters) SetDiarize(v bool) { w.p.SetDiarize(v) }
func (w *Parameters) SetDiarize(v bool) { w.p.SetDiarize(v) }
// Voice Activity Detection (VAD)
func (w *parameters) SetVAD(v bool) { w.p.SetVAD(v) }
func (w *parameters) SetVADModelPath(p string) { w.p.SetVADModelPath(p) }
func (w *parameters) SetVADThreshold(t float32) { w.p.SetVADThreshold(t) }
func (w *parameters) SetVADMinSpeechMs(ms int) { w.p.SetVADMinSpeechMs(ms) }
func (w *parameters) SetVADMinSilenceMs(ms int) { w.p.SetVADMinSilenceMs(ms) }
func (w *parameters) SetVADMaxSpeechSec(s float32) { w.p.SetVADMaxSpeechSec(s) }
func (w *parameters) SetVADSpeechPadMs(ms int) { w.p.SetVADSpeechPadMs(ms) }
func (w *parameters) SetVADSamplesOverlap(sec float32) { w.p.SetVADSamplesOverlap(sec) }
func (w *Parameters) SetVAD(v bool) { w.p.SetVAD(v) }
func (w *Parameters) SetVADModelPath(p string) { w.p.SetVADModelPath(p) }
func (w *Parameters) SetVADThreshold(t float32) { w.p.SetVADThreshold(t) }
func (w *Parameters) SetVADMinSpeechMs(ms int) { w.p.SetVADMinSpeechMs(ms) }
func (w *Parameters) SetVADMinSilenceMs(ms int) { w.p.SetVADMinSilenceMs(ms) }
func (w *Parameters) SetVADMaxSpeechSec(s float32) { w.p.SetVADMaxSpeechSec(s) }
func (w *Parameters) SetVADSpeechPadMs(ms int) { w.p.SetVADSpeechPadMs(ms) }
func (w *Parameters) SetVADSamplesOverlap(sec float32) { w.p.SetVADSamplesOverlap(sec) }
func (w *parameters) SetLanguage(lang string) error {
func (w *Parameters) SetLanguage(lang string) error {
if lang == "auto" {
return w.p.SetLanguage(-1)
}
@ -67,12 +97,12 @@ func (w *parameters) SetLanguage(lang string) error {
return w.p.SetLanguage(id)
}
func (w *parameters) SetSingleSegment(v bool) {
func (w *Parameters) SetSingleSegment(v bool) {
w.p.SetSingleSegment(v)
}
// Getter methods for Parameters interface
func (w *parameters) Language() string {
func (w *Parameters) Language() string {
id := w.p.Language()
if id == -1 {
return "auto"
@ -81,12 +111,10 @@ func (w *parameters) Language() string {
return whisper.Whisper_lang_str(id)
}
func (w *parameters) Threads() int {
func (w *Parameters) Threads() int {
return w.p.Threads()
}
func (w *parameters) unsafeParams() (*whisper.Params, error) {
func (w *Parameters) unsafeParams() (*whisper.Params, error) {
return w.p, nil
}
var _ Parameters = &parameters{}