refactor(go bindings): remove public method accessing unsafe whisper
This commit is contained in:
parent
2f16f8039d
commit
01f5c6b708
|
|
@ -3,7 +3,6 @@ package whisper
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
|
@ -14,13 +13,13 @@ import (
|
|||
|
||||
type context struct {
|
||||
n int
|
||||
model Model
|
||||
model *model
|
||||
st WhisperState
|
||||
params Parameters
|
||||
params *parameters
|
||||
Parameters
|
||||
}
|
||||
|
||||
func newContext(model Model, params Parameters) (Context, error) {
|
||||
func newContext(model *model, params *parameters) (Context, error) {
|
||||
c := new(context)
|
||||
c.model = model
|
||||
|
||||
|
|
@ -28,7 +27,7 @@ func newContext(model Model, params Parameters) (Context, error) {
|
|||
c.Parameters = c.params
|
||||
|
||||
// allocate isolated state per context
|
||||
ctx, err := model.WhisperContext().UnsafeContext()
|
||||
ctx, err := model.whisperContext().unsafeContext()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -46,7 +45,7 @@ func newContext(model Model, params Parameters) (Context, error) {
|
|||
|
||||
// DetectedLanguage returns the detected language for the current context data
|
||||
func (context *context) DetectedLanguage() string {
|
||||
ctx, err := context.model.WhisperContext().UnsafeContext()
|
||||
ctx, err := context.model.whisperContext().unsafeContext()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
|
@ -98,7 +97,7 @@ func (context *context) SystemInfo() string {
|
|||
// Make sure to call whisper_pcm_to_mel() or whisper_set_mel() first.
|
||||
// Returns the probabilities of all languages for this context's state.
|
||||
func (context *context) WhisperLangAutoDetect(offset_ms int, n_threads int) ([]float32, error) {
|
||||
ctx, err := context.model.WhisperContext().UnsafeContext()
|
||||
ctx, err := context.model.whisperContext().unsafeContext()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -123,7 +122,7 @@ func (context *context) Process(
|
|||
callNewSegment SegmentCallback,
|
||||
callProgress ProgressCallback,
|
||||
) error {
|
||||
ctx, err := context.model.WhisperContext().UnsafeContext()
|
||||
ctx, err := context.model.whisperContext().unsafeContext()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -133,13 +132,11 @@ func (context *context) Process(
|
|||
context.params.SetSingleSegment(true)
|
||||
}
|
||||
|
||||
lowLevelParams := context.params.UnsafeParams()
|
||||
if lowLevelParams == nil {
|
||||
return fmt.Errorf("lowLevelParams is nil: %w", ErrInternalAppError)
|
||||
lowLevelParams, err := context.params.unsafeParams()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("lowLevelParams", lowLevelParams)
|
||||
|
||||
st, err := context.st.UnsafeState()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -168,7 +165,7 @@ func (context *context) Process(
|
|||
|
||||
// NextSegment returns the next segment from the context buffer
|
||||
func (context *context) NextSegment() (Segment, error) {
|
||||
ctx, err := context.model.WhisperContext().UnsafeContext()
|
||||
ctx, err := context.model.whisperContext().unsafeContext()
|
||||
if err != nil {
|
||||
return Segment{}, err
|
||||
}
|
||||
|
|
@ -277,3 +274,9 @@ func toTokensFromState(ctx *whisper.Context, st *whisper.State, n int) []Token {
|
|||
|
||||
return result
|
||||
}
|
||||
|
||||
func (context *context) Model() Model {
|
||||
return context.model
|
||||
}
|
||||
|
||||
var _ Context = (*context)(nil)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ package whisper
|
|||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
whisper "github.com/ggerganov/whisper.cpp/bindings/go"
|
||||
)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -56,8 +54,13 @@ 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
|
||||
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,
|
||||
|
|
@ -65,9 +68,14 @@ type Model interface {
|
|||
|
||||
// Return a new speech-to-text context configured via the provided function
|
||||
// and sampling strategy. The context is backed by an isolated whisper_state.
|
||||
NewContextWithParams(sampling SamplingStrategy, configure ParamsConfigure) (Context, error)
|
||||
// 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
|
||||
|
||||
// Return all languages supported.
|
||||
|
|
@ -81,6 +89,8 @@ type Model interface {
|
|||
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
|
||||
|
|
@ -113,7 +123,6 @@ type Parameters interface {
|
|||
|
||||
// Enable extra debug info (e.g., dump log_mel)
|
||||
SetDebugMode(bool)
|
||||
|
||||
// Diarization (tinydiarize)
|
||||
SetDiarize(bool)
|
||||
|
||||
|
|
@ -133,6 +142,9 @@ type Parameters interface {
|
|||
// 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
|
||||
|
|
@ -141,34 +153,39 @@ type Parameters interface {
|
|||
// Getter methods
|
||||
Language() string
|
||||
Threads() int
|
||||
|
||||
UnsafeParams() *whisper.Params
|
||||
}
|
||||
|
||||
// Context is the speech recognition context.
|
||||
type Context interface {
|
||||
io.Closer
|
||||
|
||||
// Deprecated: Use Params().SetLanguage() instead
|
||||
SetLanguage(string) error
|
||||
|
||||
// Deprecated: Use Params().SetTranslate() instead
|
||||
SetTranslate(bool)
|
||||
|
||||
// Deprecated: Use Params().SetSplitOnWord() instead
|
||||
SetSplitOnWord(bool)
|
||||
|
||||
// Deprecated: Use Params().SetThreads() instead
|
||||
SetThreads(uint)
|
||||
|
||||
// Deprecated: Use Params().SetOffset() instead
|
||||
SetOffset(time.Duration)
|
||||
|
||||
// Deprecated: Use Params().SetDuration() instead
|
||||
SetDuration(time.Duration)
|
||||
|
||||
// Deprecated: Use Params().SetTokenThreshold() instead
|
||||
SetTokenThreshold(float32)
|
||||
|
||||
// Deprecated: Use Params().SetTokenSumThreshold() instead
|
||||
SetTokenSumThreshold(float32)
|
||||
// Deprecated: Use Params().SetMaxSegmentLength() instead
|
||||
|
||||
SetMaxSegmentLength(uint)
|
||||
|
||||
// Deprecated: Use Params().SetTokenTimestamps() instead
|
||||
SetTokenTimestamps(bool)
|
||||
|
||||
|
|
@ -200,7 +217,10 @@ type Context interface {
|
|||
// Deprecated: Use Params().Language() instead
|
||||
Language() string
|
||||
|
||||
// Return true if the model is multilingual.
|
||||
// Return the model that the context is backed by
|
||||
Model() Model
|
||||
|
||||
// Deprecated: Use Model().IsMultilingual() instead
|
||||
IsMultilingual() bool
|
||||
|
||||
// Get detected language
|
||||
|
|
@ -215,37 +235,39 @@ type Context interface {
|
|||
// is reached, when io.EOF is returned.
|
||||
NextSegment() (Segment, error)
|
||||
|
||||
// Deprecated token methods - use Model.IsBEG(), Model.IsSOT(), etc. instead
|
||||
// Deprecated: Use Model.IsBEG() instead
|
||||
// Deprecated: Use Model().TokenIdentifier().IsBEG() instead
|
||||
IsBEG(Token) bool
|
||||
|
||||
// Deprecated: Use Model.IsSOT() instead
|
||||
// Deprecated: Use Model().TokenIdentifier().IsSOT() instead
|
||||
IsSOT(Token) bool
|
||||
|
||||
// Deprecated: Use Model.IsEOT() instead
|
||||
// Deprecated: Use Model().TokenIdentifier().IsEOT() instead
|
||||
IsEOT(Token) bool
|
||||
|
||||
// Deprecated: Use Model.IsPREV() instead
|
||||
// Deprecated: Use Model().TokenIdentifier().IsPREV() instead
|
||||
IsPREV(Token) bool
|
||||
|
||||
// Deprecated: Use Model.IsSOLM() instead
|
||||
// Deprecated: Use Model().TokenIdentifier().IsSOLM() instead
|
||||
IsSOLM(Token) bool
|
||||
|
||||
// Deprecated: Use Model.IsNOT() instead
|
||||
// Deprecated: Use Model().TokenIdentifier().IsNOT() instead
|
||||
IsNOT(Token) bool
|
||||
|
||||
// Deprecated: Use Model.IsLANG() instead
|
||||
// Deprecated: Use Model().TokenIdentifier().IsLANG() instead
|
||||
IsLANG(Token, string) bool
|
||||
|
||||
// Deprecated: Use Model.IsText() instead
|
||||
// Deprecated: Use Model().TokenIdentifier().IsText() instead
|
||||
IsText(Token) bool
|
||||
|
||||
// Deprecated: Use Model.PrintTimings() instead - these are model-level performance metrics
|
||||
// Deprecated: Use Model().PrintTimings() instead
|
||||
// these are model-level performance metrics
|
||||
PrintTimings()
|
||||
|
||||
// Deprecated: Use Model.ResetTimings() instead - these are model-level performance metrics
|
||||
// Deprecated: Use Model().ResetTimings() instead
|
||||
// these are model-level performance metrics
|
||||
ResetTimings()
|
||||
|
||||
// SystemInfo returns the system information
|
||||
SystemInfo() string
|
||||
|
||||
// Params returns a high-level parameters wrapper - preferred method
|
||||
|
|
@ -267,13 +289,22 @@ type Segment struct {
|
|||
Tokens []Token
|
||||
|
||||
// True if the next segment is predicted as a speaker turn (tinydiarize)
|
||||
// It works only with the diarization supporting models (like small.en-tdrz.bin) with the diarization enabled
|
||||
// using Parameters.SetDiarize(true)
|
||||
SpeakerTurnNext bool
|
||||
}
|
||||
|
||||
// Token is a text or special token
|
||||
type Token struct {
|
||||
Id int
|
||||
Text string
|
||||
P float32
|
||||
// ID of the token
|
||||
Id int
|
||||
|
||||
// Text of the token
|
||||
Text string
|
||||
|
||||
// Probability of the token
|
||||
P float32
|
||||
|
||||
// Timestamp of the token
|
||||
Start, End time.Duration
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ func (model *model) WhisperContext() WhisperContext {
|
|||
return model.ctx
|
||||
}
|
||||
|
||||
func (model *model) whisperContext() *whisperCtx {
|
||||
return model.ctx
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// STRINGIFY
|
||||
|
||||
|
|
@ -58,7 +62,7 @@ func (model *model) String() string {
|
|||
|
||||
// Return true if model is multilingual (language and translation options are supported)
|
||||
func (model *model) IsMultilingual() bool {
|
||||
ctx, err := model.ctx.UnsafeContext()
|
||||
ctx, err := model.ctx.unsafeContext()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
|
@ -68,7 +72,7 @@ func (model *model) IsMultilingual() bool {
|
|||
|
||||
// Return all recognized languages. Initially it is set to auto-detect
|
||||
func (model *model) Languages() []string {
|
||||
ctx, err := model.ctx.UnsafeContext()
|
||||
ctx, err := model.ctx.unsafeContext()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -137,8 +141,8 @@ func defaultParamsConfigure(params Parameters) {
|
|||
func (m *model) newParams(
|
||||
sampling SamplingStrategy,
|
||||
configure ParamsConfigure,
|
||||
) (Parameters, error) {
|
||||
ctx, err := m.ctx.UnsafeContext()
|
||||
) (*parameters, error) {
|
||||
ctx, err := m.ctx.unsafeContext()
|
||||
if err != nil {
|
||||
return nil, ErrModelClosed
|
||||
}
|
||||
|
|
@ -157,7 +161,7 @@ func (m *model) newParams(
|
|||
|
||||
// PrintTimings prints the model performance timings to stdout.
|
||||
func (model *model) PrintTimings() {
|
||||
ctx, err := model.ctx.UnsafeContext()
|
||||
ctx, err := model.ctx.unsafeContext()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -167,7 +171,7 @@ func (model *model) PrintTimings() {
|
|||
|
||||
// ResetTimings resets the model performance timing counters.
|
||||
func (model *model) ResetTimings() {
|
||||
ctx, err := model.ctx.UnsafeContext()
|
||||
ctx, err := model.ctx.unsafeContext()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ func TestNew(t *testing.T) {
|
|||
model, err := whisper.New(ModelPath)
|
||||
assert.NoError(err)
|
||||
assert.NotNil(model)
|
||||
defer model.Close()
|
||||
defer func() { _ = model.Close() }()
|
||||
|
||||
})
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ func TestNewContext(t *testing.T) {
|
|||
model, err := whisper.New(ModelPath)
|
||||
assert.NoError(err)
|
||||
assert.NotNil(model)
|
||||
defer model.Close()
|
||||
defer func() { _ = model.Close() }()
|
||||
|
||||
context, err := model.NewContext()
|
||||
assert.NoError(err)
|
||||
|
|
@ -55,7 +55,7 @@ func TestIsMultilingual(t *testing.T) {
|
|||
model, err := whisper.New(ModelPath)
|
||||
assert.NoError(err)
|
||||
assert.NotNil(model)
|
||||
defer model.Close()
|
||||
defer func() { _ = model.Close() }()
|
||||
|
||||
isMultilingual := model.IsMultilingual()
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ func TestLanguages(t *testing.T) {
|
|||
model, err := whisper.New(ModelPath)
|
||||
assert.NoError(err)
|
||||
assert.NotNil(model)
|
||||
defer model.Close()
|
||||
defer func() { _ = model.Close() }()
|
||||
|
||||
expectedLanguages := []string{
|
||||
"en", "zh", "de", "es", "ru", "ko", "fr", "ja", "pt", "tr", "pl",
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ type parameters struct {
|
|||
p *whisper.Params
|
||||
}
|
||||
|
||||
func newParameters(whisperParams *whisper.Params) Parameters {
|
||||
func newParameters(whisperParams *whisper.Params) *parameters {
|
||||
return ¶meters{
|
||||
p: whisperParams,
|
||||
}
|
||||
|
|
@ -85,8 +85,8 @@ func (w *parameters) Threads() int {
|
|||
return w.p.Threads()
|
||||
}
|
||||
|
||||
func (w *parameters) UnsafeParams() *whisper.Params {
|
||||
return w.p
|
||||
func (w *parameters) unsafeParams() (*whisper.Params, error) {
|
||||
return w.p, nil
|
||||
}
|
||||
|
||||
var _ Parameters = ¶meters{}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ func newTokenIdentifier(whisperContext *whisperCtx) *tokenIdentifier {
|
|||
|
||||
// Token type checking methods (model-specific vocabulary)
|
||||
func (ti *tokenIdentifier) IsBEG(t Token) (bool, error) {
|
||||
ctx, err := ti.ctx.UnsafeContext()
|
||||
ctx, err := ti.ctx.unsafeContext()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ func (ti *tokenIdentifier) IsBEG(t Token) (bool, error) {
|
|||
}
|
||||
|
||||
func (ti *tokenIdentifier) IsEOT(t Token) (bool, error) {
|
||||
ctx, err := ti.ctx.UnsafeContext()
|
||||
ctx, err := ti.ctx.unsafeContext()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -32,7 +32,7 @@ func (ti *tokenIdentifier) IsEOT(t Token) (bool, error) {
|
|||
}
|
||||
|
||||
func (ti *tokenIdentifier) IsSOT(t Token) (bool, error) {
|
||||
ctx, err := ti.ctx.UnsafeContext()
|
||||
ctx, err := ti.ctx.unsafeContext()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ func (ti *tokenIdentifier) IsSOT(t Token) (bool, error) {
|
|||
}
|
||||
|
||||
func (ti *tokenIdentifier) IsPREV(t Token) (bool, error) {
|
||||
ctx, err := ti.ctx.UnsafeContext()
|
||||
ctx, err := ti.ctx.unsafeContext()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ func (ti *tokenIdentifier) IsPREV(t Token) (bool, error) {
|
|||
}
|
||||
|
||||
func (ti *tokenIdentifier) IsSOLM(t Token) (bool, error) {
|
||||
ctx, err := ti.ctx.UnsafeContext()
|
||||
ctx, err := ti.ctx.unsafeContext()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ func (ti *tokenIdentifier) IsSOLM(t Token) (bool, error) {
|
|||
}
|
||||
|
||||
func (ti *tokenIdentifier) IsNOT(t Token) (bool, error) {
|
||||
ctx, err := ti.ctx.UnsafeContext()
|
||||
ctx, err := ti.ctx.unsafeContext()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -68,7 +68,7 @@ func (ti *tokenIdentifier) IsNOT(t Token) (bool, error) {
|
|||
}
|
||||
|
||||
func (ti *tokenIdentifier) IsLANG(t Token, lang string) (bool, error) {
|
||||
ctx, err := ti.ctx.UnsafeContext()
|
||||
ctx, err := ti.ctx.unsafeContext()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -90,7 +90,7 @@ func (ti *tokenIdentifier) IsText(t Token) (bool, error) {
|
|||
return false, nil
|
||||
}
|
||||
|
||||
ctx, err := ti.ctx.UnsafeContext()
|
||||
ctx, err := ti.ctx.unsafeContext()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,6 @@ type WhisperContext interface {
|
|||
|
||||
// IsClosed returns true if the whisper context is closed
|
||||
IsClosed() bool
|
||||
|
||||
// UnsafeContext returns the raw whisper context
|
||||
UnsafeContext() (*whisper.Context, error)
|
||||
}
|
||||
|
||||
type whisperCtx struct {
|
||||
|
|
@ -38,7 +35,7 @@ func (ctx *whisperCtx) IsClosed() bool {
|
|||
return ctx.ctx == nil
|
||||
}
|
||||
|
||||
func (ctx *whisperCtx) UnsafeContext() (*whisper.Context, error) {
|
||||
func (ctx *whisperCtx) unsafeContext() (*whisper.Context, error) {
|
||||
if ctx.IsClosed() {
|
||||
return nil, ErrModelClosed
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
package whisper
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
w "github.com/ggerganov/whisper.cpp/bindings/go"
|
||||
assert "github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const testModelPathCtx = "../../models/ggml-small.en.bin"
|
||||
|
||||
func TestWhisperCtx_NilWrapper(t *testing.T) {
|
||||
wctx := newWhisperCtx(nil)
|
||||
|
||||
assert.True(t, wctx.IsClosed())
|
||||
|
||||
raw, err := wctx.unsafeContext()
|
||||
assert.Nil(t, raw)
|
||||
require.ErrorIs(t, err, ErrModelClosed)
|
||||
|
||||
require.NoError(t, wctx.Close())
|
||||
// idempotent
|
||||
require.NoError(t, wctx.Close())
|
||||
}
|
||||
|
||||
func TestWhisperCtx_Lifecycle(t *testing.T) {
|
||||
if _, err := os.Stat(testModelPathCtx); os.IsNotExist(err) {
|
||||
t.Skip("Skipping test, model not found:", testModelPathCtx)
|
||||
}
|
||||
|
||||
raw := w.Whisper_init(testModelPathCtx)
|
||||
require.NotNil(t, raw)
|
||||
|
||||
wctx := newWhisperCtx(raw)
|
||||
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())
|
||||
|
||||
got, err = wctx.unsafeContext()
|
||||
assert.Nil(t, got)
|
||||
require.ErrorIs(t, err, ErrModelClosed)
|
||||
|
||||
// idempotent
|
||||
require.NoError(t, wctx.Close())
|
||||
// no further free; raw already freed by wctx.Close()
|
||||
}
|
||||
|
||||
func TestWhisperCtx_FromModelLifecycle(t *testing.T) {
|
||||
if _, err := os.Stat(testModelPathCtx); os.IsNotExist(err) {
|
||||
t.Skip("Skipping test, model not found:", testModelPathCtx)
|
||||
}
|
||||
|
||||
modelNew, err := New(testModelPathCtx)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, modelNew)
|
||||
|
||||
model := modelNew.(*model)
|
||||
|
||||
wc := model.whisperContext()
|
||||
require.NotNil(t, wc)
|
||||
|
||||
// Should be usable before model.Close
|
||||
raw, err := wc.unsafeContext()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, raw)
|
||||
|
||||
// Close model should close underlying context
|
||||
require.NoError(t, model.Close())
|
||||
|
||||
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())
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package whisper
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
w "github.com/ggerganov/whisper.cpp/bindings/go"
|
||||
assert "github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const testModelPathState = "../../models/ggml-small.en.bin"
|
||||
|
||||
func TestWhisperState_NilWrapper(t *testing.T) {
|
||||
ws := newWhisperState(nil)
|
||||
|
||||
state, err := ws.UnsafeState()
|
||||
assert.Nil(t, state)
|
||||
require.ErrorIs(t, err, ErrModelClosed)
|
||||
|
||||
require.NoError(t, ws.Close())
|
||||
// idempotent
|
||||
require.NoError(t, ws.Close())
|
||||
}
|
||||
|
||||
func TestWhisperState_Lifecycle(t *testing.T) {
|
||||
if _, err := os.Stat(testModelPathState); os.IsNotExist(err) {
|
||||
t.Skip("Skipping test, model not found:", testModelPathState)
|
||||
}
|
||||
|
||||
ctx := w.Whisper_init(testModelPathState)
|
||||
require.NotNil(t, ctx)
|
||||
defer ctx.Whisper_free()
|
||||
|
||||
state := ctx.Whisper_init_state()
|
||||
require.NotNil(t, state)
|
||||
|
||||
ws := newWhisperState(state)
|
||||
|
||||
got, err := ws.UnsafeState()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
|
||||
// close frees underlying state and marks closed
|
||||
require.NoError(t, ws.Close())
|
||||
|
||||
got, err = ws.UnsafeState()
|
||||
assert.Nil(t, got)
|
||||
require.ErrorIs(t, err, ErrModelClosed)
|
||||
|
||||
// idempotent
|
||||
require.NoError(t, ws.Close())
|
||||
}
|
||||
Loading…
Reference in New Issue