mirror of
https://github.com/mudler/LocalAI.git
synced 2026-09-25 07:34:58 -04:00
feat(kimodocpp): add API and backend request observability Capture animation requests, phase timings, output metadata, and failures in traces. Record correct API error statuses and cover completed, running, failed, and disabled tracing. Assisted-by: Codex:GPT-6 Signed-off-by: Richard Palethorpe <io@richiejp.com>
108 lines
3.2 KiB
Go
108 lines
3.2 KiB
Go
// SPDX-License-Identifier: MIT
|
|
package backend
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"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"
|
|
)
|
|
|
|
func Model3DAnimation(ctx context.Context, request *proto.Animate3DRequest, loader *model.ModelLoader, modelConfig config.ModelConfig, appConfig *config.ApplicationConfig) (responseMetadata []byte, err error) {
|
|
var entry *trace.BackendTrace
|
|
if appConfig.EnableTracing {
|
|
trace.InitBackendTracingIfEnabled(appConfig.TracingMaxItems, appConfig.TracingMaxBodyBytes)
|
|
inputs := map[string]any{}
|
|
for name, input := range request.Inputs {
|
|
if input == nil {
|
|
continue
|
|
}
|
|
detail := map[string]any{"type": input.Type}
|
|
if input.Type == "text" {
|
|
detail["text"] = input.Data
|
|
} else {
|
|
detail["present"] = input.Data != ""
|
|
}
|
|
inputs[name] = detail
|
|
}
|
|
params := map[string]any{}
|
|
for name, value := range request.Params {
|
|
params[name] = value
|
|
}
|
|
summary := "3d: animate"
|
|
if prompt := request.Inputs["prompt"]; prompt != nil && prompt.Type == "text" {
|
|
summary += ": " + prompt.Data
|
|
}
|
|
entry = &trace.BackendTrace{Timestamp: time.Now(), Type: trace.BackendTrace3DAnimation,
|
|
ModelName: modelConfig.Name, Backend: modelConfig.Backend, Summary: trace.TruncateString(summary, 200),
|
|
Data: map[string]any{"inputs": inputs, "params": params, "model_file": modelConfig.Model, "stage": "loading_model"}}
|
|
entry.ID = trace.BeginBackendTrace(*entry)
|
|
defer trace.CancelBackendTrace(entry.ID)
|
|
defer func() {
|
|
entry.Duration = time.Since(entry.Timestamp)
|
|
if err != nil {
|
|
entry.Error = err.Error()
|
|
}
|
|
if len(responseMetadata) > 0 {
|
|
var metadata map[string]any
|
|
if decodeErr := json.Unmarshal(responseMetadata, &metadata); decodeErr != nil {
|
|
entry.Data["metadata_error"] = decodeErr.Error()
|
|
entry.Data["metadata_raw"] = string(responseMetadata)
|
|
} else {
|
|
entry.Data["metadata"] = metadata
|
|
}
|
|
}
|
|
trace.RecordBackendTrace(*entry)
|
|
}()
|
|
}
|
|
phaseStart := time.Now()
|
|
inferenceModel, err := loader.Load(ModelOptions(modelConfig, appConfig)...)
|
|
if entry != nil {
|
|
entry.Data["load_ms"] = time.Since(phaseStart).Milliseconds()
|
|
}
|
|
if err != nil {
|
|
recordModelLoadFailure(appConfig, modelConfig.Name, modelConfig.Backend, err, nil)
|
|
return nil, err
|
|
}
|
|
phaseStart = time.Now()
|
|
if entry != nil {
|
|
entry.Data["stage"] = "waiting_for_slot"
|
|
}
|
|
release, err := AcquireGlobalBackendSlot()
|
|
if entry != nil {
|
|
entry.Data["queue_ms"] = time.Since(phaseStart).Milliseconds()
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer release()
|
|
if entry != nil {
|
|
entry.Data["stage"] = "inference"
|
|
}
|
|
phaseStart = time.Now()
|
|
request.ModelIdentity = modelConfig.Model
|
|
result, err := inferenceModel.Animate3D(ctx, request)
|
|
if entry != nil {
|
|
entry.Data["inference_ms"] = time.Since(phaseStart).Milliseconds()
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if result == nil || !result.Success {
|
|
return nil, fmt.Errorf("animation backend failed: %s", result.GetMessage())
|
|
}
|
|
if entry != nil {
|
|
entry.Data["stage"] = "completed"
|
|
if info, statErr := os.Stat(request.Dst); statErr == nil {
|
|
entry.Data["output_bytes"] = info.Size()
|
|
}
|
|
}
|
|
return result.Metadata, nil
|
|
}
|