diff --git a/bindings/go/pkg/whisper/consts.go b/bindings/go/pkg/whisper/consts.go index c6d5ad388..eab223ce5 100644 --- a/bindings/go/pkg/whisper/consts.go +++ b/bindings/go/pkg/whisper/consts.go @@ -19,8 +19,12 @@ var ( ErrProcessingFailed = errors.New("processing failed") ErrUnsupportedLanguage = errors.New("unsupported language") ErrModelNotMultilingual = errors.New("model is not multilingual") - ErrUnableToCreateState = errors.New("unable to create state") ErrModelClosed = errors.Join(errors.New("model has been closed"), ErrInternalAppError) + + // Private errors + errParametersRequired = errors.New("parameters are required") + errModelRequired = errors.New("model is required") + errUnableToCreateState = errors.New("unable to create state") ) /////////////////////////////////////////////////////////////////////////////// diff --git a/bindings/go/pkg/whisper/context.go b/bindings/go/pkg/whisper/context.go index 6f162877f..ad0ce5c46 100644 --- a/bindings/go/pkg/whisper/context.go +++ b/bindings/go/pkg/whisper/context.go @@ -14,11 +14,19 @@ import ( type context struct { n int model *model - st WhisperState + st *whisperState *Parameters } func NewContext(model *model, params *Parameters) (*context, error) { + if model == nil { + return nil, errModelRequired + } + + if params == nil { + return nil, errParametersRequired + } + c := new(context) c.model = model @@ -32,7 +40,7 @@ func NewContext(model *model, params *Parameters) (*context, error) { st := ctx.Whisper_init_state() if st == nil { - return nil, ErrUnableToCreateState + return nil, errUnableToCreateState } c.st = newWhisperState(st) @@ -48,7 +56,7 @@ func (context *context) DetectedLanguage() string { return "" } - st, err := context.st.UnsafeState() + st, err := context.st.unsafeState() if err != nil { return "" } @@ -62,7 +70,7 @@ func (context *context) DetectedLanguage() string { // Close frees the whisper state and marks the context as closed. func (context *context) Close() error { - return context.st.Close() + return context.st.close() } // Params returns a high-level parameters wrapper @@ -100,7 +108,7 @@ func (context *context) WhisperLangAutoDetect(offset_ms int, n_threads int) ([]f return nil, err } - st, err := context.st.UnsafeState() + st, err := context.st.unsafeState() if err != nil { return nil, err } @@ -135,7 +143,7 @@ func (context *context) Process( return err } - st, err := context.st.UnsafeState() + st, err := context.st.unsafeState() if err != nil { return err } @@ -168,7 +176,7 @@ func (context *context) NextSegment() (Segment, error) { return Segment{}, err } - st, err := context.st.UnsafeState() + st, err := context.st.unsafeState() if err != nil { return Segment{}, err } @@ -190,48 +198,48 @@ func (context *context) IsMultilingual() bool { // Token helpers // Deprecated: Use Model.IsText() instead - token checking is model-specific. func (context *context) IsText(t Token) bool { - result, _ := context.model.TokenIdentifier().IsText(t) + result, _ := context.model.tokenIdentifier().IsText(t) return result } // Deprecated: Use Model.IsBEG() instead - token checking is model-specific. func (context *context) IsBEG(t Token) bool { - result, _ := context.model.TokenIdentifier().IsBEG(t) + result, _ := context.model.tokenIdentifier().IsBEG(t) return result } // Deprecated: Use Model.IsSOT() instead - token checking is model-specific. func (context *context) IsSOT(t Token) bool { - result, _ := context.model.TokenIdentifier().IsSOT(t) + result, _ := context.model.tokenIdentifier().IsSOT(t) return result } // Deprecated: Use Model.IsEOT() instead - token checking is model-specific. func (context *context) IsEOT(t Token) bool { - result, _ := context.model.TokenIdentifier().IsEOT(t) + result, _ := context.model.tokenIdentifier().IsEOT(t) return result } // Deprecated: Use Model.IsPREV() instead - token checking is model-specific. func (context *context) IsPREV(t Token) bool { - result, _ := context.model.TokenIdentifier().IsPREV(t) + result, _ := context.model.tokenIdentifier().IsPREV(t) return result } // Deprecated: Use Model.IsSOLM() instead - token checking is model-specific. func (context *context) IsSOLM(t Token) bool { - result, _ := context.model.TokenIdentifier().IsSOLM(t) + result, _ := context.model.tokenIdentifier().IsSOLM(t) return result } // Deprecated: Use Model.IsNOT() instead - token checking is model-specific. func (context *context) IsNOT(t Token) bool { - result, _ := context.model.TokenIdentifier().IsNOT(t) + result, _ := context.model.tokenIdentifier().IsNOT(t) return result } func (context *context) SetLanguage(lang string) error { - if context.model.whisperContext().IsClosed() { + if context.model.whisperContext().isClosed() { // TODO: remove this logic after deprecating the ErrInternalAppError return ErrModelClosed } @@ -245,7 +253,7 @@ func (context *context) SetLanguage(lang string) error { // Deprecated: Use Model.IsLANG() instead - token checking is model-specific. func (context *context) IsLANG(t Token, lang string) bool { - result, _ := context.model.TokenIdentifier().IsLANG(t, lang) + result, _ := context.model.tokenIdentifier().IsLANG(t, lang) return result } diff --git a/bindings/go/pkg/whisper/context_test.go b/bindings/go/pkg/whisper/context_test.go index 70f941b29..02f918b38 100644 --- a/bindings/go/pkg/whisper/context_test.go +++ b/bindings/go/pkg/whisper/context_test.go @@ -329,7 +329,11 @@ func TestContext_VAD_And_Diarization_Params_DoNotPanic(t *testing.T) { assert.NoError(err) defer func() { _ = model.Close() }() - ctx, err := whisper.NewContext(model, nil) + params, err := whisper.NewParameters(model, whisper.SAMPLING_GREEDY, nil) + assert.NoError(err) + assert.NotNil(params) + + ctx, err := whisper.NewContext(model, params) assert.NoError(err) defer func() { _ = ctx.Close() }() diff --git a/bindings/go/pkg/whisper/interface.go b/bindings/go/pkg/whisper/interface.go index 9a0cecfb8..a4d7db8b0 100644 --- a/bindings/go/pkg/whisper/interface.go +++ b/bindings/go/pkg/whisper/interface.go @@ -20,32 +20,6 @@ type ProgressCallback func(int) // continue processing. It is called during the Process function type EncoderBeginCallback func() bool -type TokenIdentifier interface { - // Test for "begin" token - IsBEG(Token) (bool, error) - - // Test for "start of transcription" token - IsSOT(Token) (bool, error) - - // Test for "end of transcription" token - IsEOT(Token) (bool, error) - - // Test for "start of prev" token - IsPREV(Token) (bool, error) - - // Test for "start of lm" token - IsSOLM(Token) (bool, error) - - // Test for "no timestamps" token - IsNOT(Token) (bool, error) - - // Test for token associated with a specific language - IsLANG(Token, string) (bool, error) - - // Test for text token - IsText(Token) (bool, error) -} - type ParamsConfigure func(*Parameters) // Model is the interface to a whisper model. Create a new model with the @@ -74,65 +48,8 @@ type Model interface { ResetTimings() } -// // 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) - -// // 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) - -// // Set the temperature -// SetTemperature(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 single segment mode -// SetSingleSegment(bool) - -// // Getter methods -// Language() string -// Threads() int -// } - // Context is the speech recognition context. +// Deprecated: Use NewContext implementation struct instead of relying on this interface type Context interface { io.Closer diff --git a/bindings/go/pkg/whisper/model.go b/bindings/go/pkg/whisper/model.go index d9aabdaf7..7ce314324 100644 --- a/bindings/go/pkg/whisper/model.go +++ b/bindings/go/pkg/whisper/model.go @@ -9,9 +9,9 @@ import ( ) type model struct { - path string - ctx *whisperCtx - tokenIdentifier *tokenIdentifier + path string + ctx *whisperCtx + tokId *tokenIdentifier } // Make sure model adheres to the interface @@ -33,7 +33,7 @@ func NewModel( return nil, ErrUnableToLoadModel } else { model.ctx = newWhisperCtx(ctx) - model.tokenIdentifier = newTokenIdentifier(model.ctx) + model.tokId = newTokenIdentifier(model.ctx) model.path = path } @@ -42,20 +42,13 @@ func NewModel( } func (model *model) Close() error { - return model.ctx.Close() -} - -func (model *model) WhisperContext() WhisperContext { - return model.ctx + return model.ctx.close() } func (model *model) whisperContext() *whisperCtx { return model.ctx } -/////////////////////////////////////////////////////////////////////////////// -// STRINGIFY - func (model *model) String() string { str := "" } -/////////////////////////////////////////////////////////////////////////////// -// PUBLIC METHODS - // Return true if model is multilingual (language and translation options are supported) func (model *model) IsMultilingual() bool { ctx, err := model.ctx.unsafeContext() @@ -132,7 +122,6 @@ func (model *model) ResetTimings() { ctx.Whisper_reset_timings() } -// WhisperContext returns the low-level whisper context, or error if the model is closed. -func (model *model) TokenIdentifier() TokenIdentifier { - return model.tokenIdentifier +func (model *model) tokenIdentifier() *tokenIdentifier { + return model.tokId } diff --git a/bindings/go/pkg/whisper/whisper_ctx.go b/bindings/go/pkg/whisper/whisper_ctx.go index bdc166cf8..d84e84605 100644 --- a/bindings/go/pkg/whisper/whisper_ctx.go +++ b/bindings/go/pkg/whisper/whisper_ctx.go @@ -2,14 +2,6 @@ package whisper import whisper "github.com/ggerganov/whisper.cpp/bindings/go" -type WhisperContext interface { - // Close closes the whisper context - Close() error - - // IsClosed returns true if the whisper context is closed - IsClosed() bool -} - type whisperCtx struct { ctx *whisper.Context } @@ -20,7 +12,7 @@ func newWhisperCtx(ctx *whisper.Context) *whisperCtx { } } -func (ctx *whisperCtx) Close() error { +func (ctx *whisperCtx) close() error { if ctx.ctx == nil { return nil } @@ -31,16 +23,14 @@ func (ctx *whisperCtx) Close() error { return nil } -func (ctx *whisperCtx) IsClosed() bool { +func (ctx *whisperCtx) isClosed() bool { return ctx.ctx == nil } func (ctx *whisperCtx) unsafeContext() (*whisper.Context, error) { - if ctx.IsClosed() { + if ctx.isClosed() { return nil, ErrModelClosed } return ctx.ctx, nil } - -var _ WhisperContext = (*whisperCtx)(nil) diff --git a/bindings/go/pkg/whisper/whisper_ctx_test.go b/bindings/go/pkg/whisper/whisper_ctx_test.go index 68a094bd2..0a3679db2 100644 --- a/bindings/go/pkg/whisper/whisper_ctx_test.go +++ b/bindings/go/pkg/whisper/whisper_ctx_test.go @@ -14,15 +14,15 @@ const testModelPathCtx = "../../models/ggml-small.en.bin" func TestWhisperCtx_NilWrapper(t *testing.T) { wctx := newWhisperCtx(nil) - assert.True(t, wctx.IsClosed()) + assert.True(t, wctx.isClosed()) raw, err := wctx.unsafeContext() assert.Nil(t, raw) require.ErrorIs(t, err, ErrModelClosed) - require.NoError(t, wctx.Close()) + require.NoError(t, wctx.close()) // idempotent - require.NoError(t, wctx.Close()) + require.NoError(t, wctx.close()) } func TestWhisperCtx_Lifecycle(t *testing.T) { @@ -34,22 +34,22 @@ func TestWhisperCtx_Lifecycle(t *testing.T) { require.NotNil(t, raw) wctx := newWhisperCtx(raw) - assert.False(t, wctx.IsClosed()) + assert.False(t, wctx.isClosed()) got, err := wctx.unsafeContext() require.NoError(t, err) require.NotNil(t, got) // close frees underlying ctx and marks closed - require.NoError(t, wctx.Close()) - assert.True(t, wctx.IsClosed()) + require.NoError(t, wctx.close()) + assert.True(t, wctx.isClosed()) got, err = wctx.unsafeContext() assert.Nil(t, got) require.ErrorIs(t, err, ErrModelClosed) // idempotent - require.NoError(t, wctx.Close()) + require.NoError(t, wctx.close()) // no further free; raw already freed by wctx.Close() } @@ -75,11 +75,11 @@ func TestWhisperCtx_FromModelLifecycle(t *testing.T) { // Close model should close underlying context require.NoError(t, model.Close()) - assert.True(t, wc.IsClosed()) + assert.True(t, wc.isClosed()) raw, err = wc.unsafeContext() assert.Nil(t, raw) require.ErrorIs(t, err, ErrModelClosed) // Idempotent close on wrapper - require.NoError(t, wc.Close()) + require.NoError(t, wc.close()) } diff --git a/bindings/go/pkg/whisper/whisper_state.go b/bindings/go/pkg/whisper/whisper_state.go index 24ca4ed44..cee489487 100644 --- a/bindings/go/pkg/whisper/whisper_state.go +++ b/bindings/go/pkg/whisper/whisper_state.go @@ -2,22 +2,17 @@ package whisper import whisper "github.com/ggerganov/whisper.cpp/bindings/go" -type WhisperState interface { - Close() error - UnsafeState() (*whisper.State, error) -} - type whisperState struct { state *whisper.State } -func newWhisperState(state *whisper.State) WhisperState { +func newWhisperState(state *whisper.State) *whisperState { return &whisperState{ state: state, } } -func (s *whisperState) Close() error { +func (s *whisperState) close() error { if s.state == nil { return nil } @@ -28,7 +23,7 @@ func (s *whisperState) Close() error { return nil } -func (s *whisperState) UnsafeState() (*whisper.State, error) { +func (s *whisperState) unsafeState() (*whisper.State, error) { if s.state == nil { return nil, ErrModelClosed } diff --git a/bindings/go/pkg/whisper/whisper_state_test.go b/bindings/go/pkg/whisper/whisper_state_test.go index 37d828acb..2c4c6dd30 100644 --- a/bindings/go/pkg/whisper/whisper_state_test.go +++ b/bindings/go/pkg/whisper/whisper_state_test.go @@ -14,13 +14,13 @@ const testModelPathState = "../../models/ggml-small.en.bin" func TestWhisperState_NilWrapper(t *testing.T) { ws := newWhisperState(nil) - state, err := ws.UnsafeState() + state, err := ws.unsafeState() assert.Nil(t, state) require.ErrorIs(t, err, ErrModelClosed) - require.NoError(t, ws.Close()) + require.NoError(t, ws.close()) // idempotent - require.NoError(t, ws.Close()) + require.NoError(t, ws.close()) } func TestWhisperState_Lifecycle(t *testing.T) { @@ -37,17 +37,17 @@ func TestWhisperState_Lifecycle(t *testing.T) { ws := newWhisperState(state) - got, err := ws.UnsafeState() + got, err := ws.unsafeState() require.NoError(t, err) require.NotNil(t, got) // close frees underlying state and marks closed - require.NoError(t, ws.Close()) + require.NoError(t, ws.close()) - got, err = ws.UnsafeState() + got, err = ws.unsafeState() assert.Nil(t, got) require.ErrorIs(t, err, ErrModelClosed) // idempotent - require.NoError(t, ws.Close()) + require.NoError(t, ws.close()) }