Files
LocalAI/core/backend/model3d.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

117 lines
3.2 KiB
Go

package backend
import (
"maps"
"time"
"github.com/mudler/LocalAI/core/config"
"github.com/mudler/LocalAI/core/trace"
"github.com/mudler/LocalAI/pkg/grpc/proto"
model "github.com/mudler/LocalAI/pkg/model"
)
// Model3DGenerationOptions is the backend-neutral request passed to 3D
// generators. Image contains a staged local path by the time it reaches
// this layer.
type Model3DGenerationOptions struct {
Image string
Destination string
Seed int32
Step int32
CFGScale float32
TextureSteps int32
Quality string
Background string
Params map[string]string
}
func Model3DGeneration(options Model3DGenerationOptions, loader *model.ModelLoader, modelConfig config.ModelConfig, appConfig *config.ApplicationConfig) (func() error, error) {
opts := ModelOptions(modelConfig, appConfig)
inferenceModel, err := loader.Load(opts...)
if err != nil {
recordModelLoadFailure(appConfig, modelConfig.Name, modelConfig.Backend, err, nil)
return nil, err
}
fn := func() error {
_, err := inferenceModel.Generate3D(
appConfig.Context,
&proto.Generate3DRequest{
Src: options.Image,
Dst: options.Destination,
Seed: options.Seed,
Step: options.Step,
CfgScale: options.CFGScale,
TextureSteps: options.TextureSteps,
Quality: options.Quality,
Background: options.Background,
Params: maps.Clone(options.Params),
},
)
return err
}
if appConfig.EnableTracing {
trace.InitBackendTracingIfEnabled(appConfig.TracingMaxItems, appConfig.TracingMaxBodyBytes)
traceType := trace.BackendTrace3DGeneration
traceSummary := "3d: " + options.Quality
traceData := map[string]any{}
if options.Params["operation"] == "print_remesh" {
traceType = trace.BackendTrace3DRemesh
traceSummary = "3d: remesh"
traceData["detail_percent"] = options.Params["detail_percent"]
traceData["has_mesh"] = options.Image != ""
} else {
traceData = map[string]any{
"seed": options.Seed,
"step": options.Step,
"cfg_scale": options.CFGScale,
"texture_steps": options.TextureSteps,
"quality": options.Quality,
"background": options.Background,
"has_image": options.Image != "",
}
}
originalFn := fn
fn = func() error {
startTime := time.Now()
traceID := trace.BeginBackendTrace(trace.BackendTrace{Timestamp: startTime, Type: traceType, ModelName: modelConfig.Name, Backend: modelConfig.Backend, Summary: trace.TruncateString(traceSummary, 200)})
defer trace.CancelBackendTrace(traceID)
err := originalFn()
duration := time.Since(startTime)
errStr := ""
if err != nil {
errStr = err.Error()
}
trace.RecordBackendTrace(trace.BackendTrace{
ID: traceID,
Timestamp: startTime,
Duration: duration,
Type: traceType,
ModelName: modelConfig.Name,
Backend: modelConfig.Backend,
Summary: trace.TruncateString(traceSummary, 200),
Error: errStr,
Data: traceData,
})
return err
}
}
originalFn := fn
fn = func() error {
release, err := AcquireGlobalBackendSlot()
if err != nil {
return err
}
defer release()
return originalFn()
}
return fn, nil
}