whisper.cpp/bindings/go/pkg/whisper/model.go

180 lines
4.2 KiB
Go

package whisper
import (
"fmt"
"os"
// Bindings
low "github.com/ggerganov/whisper.cpp/bindings/go"
)
type ModelContext struct {
path string
ca *ctxAccessor
tokId *tokenIdentifier
}
// Make sure model adheres to the interface
var _ Model = (*ModelContext)(nil)
// Timings is a compact, high-level timing snapshot in milliseconds
type Timings struct {
SampleMS float32
EncodeMS float32
DecodeMS float32
BatchdMS float32
PromptMS float32
}
// Deprecated: Use NewModelContext instead
func New(path string) (Model, error) {
return NewModelContext(path)
}
// NewModelContext creates a new model context
func NewModelContext(
path string,
) (*ModelContext, error) {
return NewModelContextWithParams(
path,
NewModelContextParams(),
)
}
// NewModelContextWithParams creates a new model context with custom initialization params
func NewModelContextWithParams(
path string,
params ModelContextParams,
) (*ModelContext, error) {
model := new(ModelContext)
if _, err := os.Stat(path); err != nil {
return nil, err
}
ctx := low.Whisper_init_with_params(path, params.toLow())
if ctx == nil {
return nil, ErrUnableToLoadModel
}
model.ca = newCtxAccessor(ctx)
model.tokId = newTokenIdentifier(model.ca)
model.path = path
return model, nil
}
func (model *ModelContext) Close() error {
return model.ca.close()
}
func (model *ModelContext) ctxAccessor() *ctxAccessor {
return model.ca
}
func (model *ModelContext) String() string {
str := "<whisper.model"
if model.ca != nil {
str += fmt.Sprintf(" model=%q", model.path)
}
return str + ">"
}
// Return true if model is multilingual (language and translation options are supported)
func (model *ModelContext) IsMultilingual() bool {
ctx, err := model.ca.context()
if err != nil {
return false
}
return ctx.Whisper_is_multilingual() != 0
}
// Return all recognized languages. Initially it is set to auto-detect
func (model *ModelContext) Languages() []string {
ctx, err := model.ca.context()
if err != nil {
return nil
}
result := make([]string, 0, low.Whisper_lang_max_id())
for i := 0; i < low.Whisper_lang_max_id(); i++ {
str := low.Whisper_lang_str(i)
if ctx.Whisper_lang_id(str) >= 0 {
result = append(result, str)
}
}
return result
}
// NewContext creates a new speech-to-text context.
// Each context is backed by an isolated whisper_state for safe concurrent processing.
func (model *ModelContext) NewContext() (Context, error) {
// Create new context with default params
params, err := NewParameters(model, SAMPLING_GREEDY, nil)
if err != nil {
return nil, err
}
// Return new context (stateless for backward compatibility with timings)
return NewStatelessContext(
model,
params,
)
}
// PrintTimings prints the model performance timings to stdout.
func (model *ModelContext) PrintTimings() {
ctx, err := model.ca.context()
if err != nil {
return
}
ctx.Whisper_print_timings()
}
// ResetTimings resets the model performance timing counters.
func (model *ModelContext) ResetTimings() {
ctx, err := model.ca.context()
if err != nil {
return
}
ctx.Whisper_reset_timings()
}
// GetTimings returns a compact snapshot of model-level processing timings.
//
// Behavior notes:
// - Stateless contexts (created via ModelContext.NewContext or NewStatelessContext)
// update model-level timings during Process. After a stateless Process call,
// the returned timings are expected to be non-zero (ok == true).
// - Stateful contexts (created via NewStatefulContext) use a per-state backend
// and do not affect model-level timings. After a stateful Process call,
// the returned timings are expected to be zero values (fields equal 0) or
// the call may return ok == false depending on the underlying implementation.
//
// Use ResetTimings before measurement to clear previous values.
func (model *ModelContext) GetTimings() (Timings, bool) {
ctx, err := model.ca.context()
if err != nil {
return Timings{}, false
}
if t, ok := ctx.Whisper_get_timings_go(); ok {
return Timings{
SampleMS: t.SampleMS,
EncodeMS: t.EncodeMS,
DecodeMS: t.DecodeMS,
BatchdMS: t.BatchdMS,
PromptMS: t.PromptMS,
}, true
}
return Timings{}, false
}
func (model *ModelContext) tokenIdentifier() *tokenIdentifier {
return model.tokId
}