Files
LocalAI/core/backend/voice_embed.go
T
Richard Palethorpe 799cc9f211 feat: bound global admission and expose running backend traces (#11560)
feat: bound backend admission and expose running traces

Add process-wide backend execution admission without blocking UI or administrative HTTP work. Represent backend operations while they are in flight, surface running traces with immediate log links, and tie streaming admission leases to the gRPC receive lifecycle.

Assisted-by: OpenAI Codex: GPT-5

Signed-off-by: Richard Palethorpe <io@richiejp.com>
2026-08-18 08:56:59 +02:00

78 lines
2.2 KiB
Go

package backend
import (
"context"
"fmt"
"time"
"github.com/mudler/LocalAI/core/config"
"github.com/mudler/LocalAI/core/trace"
"github.com/mudler/LocalAI/pkg/grpc/proto"
"github.com/mudler/LocalAI/pkg/model"
)
// VoiceEmbed returns a speaker embedding (typically 192-d for ECAPA-TDNN)
// for the audio file at audioPath. Unlike ModelEmbedding (which is
// OpenAI-compatible and text-only), this call takes an audio path and
// returns the backend's speaker-encoder output.
func VoiceEmbed(
ctx context.Context,
audioPath string,
loader *model.ModelLoader,
appConfig *config.ApplicationConfig,
modelConfig config.ModelConfig,
) (*proto.VoiceEmbedResponse, error) {
opts := ModelOptions(modelConfig, appConfig)
voiceModel, err := loader.Load(opts...)
if err != nil {
recordModelLoadFailure(appConfig, modelConfig.Name, modelConfig.Backend, err, nil)
return nil, err
}
if voiceModel == nil {
return nil, fmt.Errorf("could not load voice recognition model")
}
release, err := AcquireGlobalBackendSlot()
if err != nil {
return nil, err
}
defer release()
var startTime time.Time
var traceID string
if appConfig.EnableTracing {
trace.InitBackendTracingIfEnabled(appConfig.TracingMaxItems, appConfig.TracingMaxBodyBytes)
startTime = time.Now()
traceID = trace.BeginBackendTrace(trace.BackendTrace{Timestamp: startTime, Type: trace.BackendTraceVoiceEmbed, ModelName: modelConfig.Name, Backend: modelConfig.Backend, Summary: "voice embedding"})
}
defer trace.CancelBackendTrace(traceID)
res, err := voiceModel.VoiceEmbed(ctx, &proto.VoiceEmbedRequest{
ModelIdentity: modelConfig.Model,
Audio: audioPath,
})
if appConfig.EnableTracing {
errStr := ""
if err != nil {
errStr = err.Error()
}
trace.RecordBackendTrace(trace.BackendTrace{
ID: traceID,
Timestamp: startTime,
Duration: time.Since(startTime),
Type: trace.BackendTraceVoiceEmbed,
ModelName: modelConfig.Name,
Backend: modelConfig.Backend,
Error: errStr,
})
}
if err != nil {
return nil, err
}
if res == nil || len(res.Embedding) == 0 {
return nil, fmt.Errorf("voice embedding returned empty vector (no speech detected?)")
}
return res, nil
}