mirror of
https://github.com/mudler/LocalAI.git
synced 2026-09-13 06:45:26 -04:00
None was introduced by this branch and all three are in test code, which is what made them survive: every suite passed on every run and only the race detector said otherwise. A known-failing -race run is worse than a noisy one, because a real race raised by production code lands in the same report and is read as one of these. galleryop: gatedModelManager guarded the recorded names and not the gate channel itself. A spec frees the parked worker by closing the gate and installing a fresh one, on the spec goroutine, while the worker goroutine reads the field to park on it. The channel is now read and replaced under the same mutex, and cleanup closes idempotently. pkg/model: two specs swapped xlog's package logger to capture output and swapped it back on cleanup. xlog.SetLogger writes an unsynchronised global, so the restore raced with the backend process watcher, which logs while a process is stopping; the captured bytes.Buffer was written by that goroutine and read by an Eventually at the same time. SetLogger is now called once for the whole test binary, from init, before a goroutine exists to race with, and a spec swaps the DESTINATION under a mutex through a routing slog.Handler. Per-spec level filtering is preserved deliberately: one of these specs asserts that a debug emission is filtered OUT and would pass vacuously against a handler that recorded everything. openai: fakeTransport appended to its event and audio logs from the response and turn coordinators' goroutines while a spec ranged over them. Both are behind a mutex and are read through snapshot accessors; the fields are renamed so a raw read from another spec file does not compile. Assisted-by: Claude Opus 5 [claude-code] Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
95 lines
3.3 KiB
Go
95 lines
3.3 KiB
Go
package model
|
|
|
|
import (
|
|
"log/slog"
|
|
|
|
pb "github.com/mudler/LocalAI/pkg/grpc/proto"
|
|
"github.com/mudler/LocalAI/pkg/system"
|
|
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
// backendLoader is not a synonym for "a model is being loaded". In distributed
|
|
// mode Load() deliberately bypasses the local cache and calls backendLoader on
|
|
// every inference request so SmartRouter can re-pick a replica per request, so
|
|
// the function runs at request rate against an already-resident model. Emitting
|
|
// the load banner unconditionally made ordinary embedding traffic (~5 req/s)
|
|
// look like a retry storm in the INFO log and sent an engineer chasing a
|
|
// non-existent hot loop during an unrelated production investigation. INFO must
|
|
// mark a genuine cold load only; the per-call trace belongs at DEBUG.
|
|
var _ = Describe("backendLoader load logging", func() {
|
|
var (
|
|
ml *ModelLoader
|
|
captured *syncBuffer
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
systemState, err := system.GetSystemState(system.WithModelPath(GinkgoT().TempDir()))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
ml = NewModelLoader(systemState)
|
|
|
|
// Capture at info level so a debug-level emission is filtered out: the
|
|
// assertions then fail on severity, not merely on wording.
|
|
captured = captureLogs(slog.LevelInfo)
|
|
})
|
|
|
|
AfterEach(stopCapturingLogs)
|
|
|
|
Context("when the model is already resident", func() {
|
|
BeforeEach(func() {
|
|
resident := NewModel("resident-model", "127.0.0.1:65535", nil)
|
|
// Skip the gRPC health probe so the resident model survives the
|
|
// lookup without a live backend behind the address.
|
|
resident.MarkHealthy()
|
|
ml.mu.Lock()
|
|
ml.store.Set("resident-model", resident)
|
|
ml.mu.Unlock()
|
|
})
|
|
|
|
It("does not announce a load at info level", func() {
|
|
_, err := ml.backendLoader(
|
|
WithModelID("resident-model"),
|
|
WithModel("resident-model"),
|
|
WithBackendString("llama-cpp"),
|
|
WithLoadGRPCLoadModelOpts(&pb.ModelOptions{ContextSize: 4096}),
|
|
)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
Expect(captured.String()).ToNot(ContainSubstring("BackendLoader starting"),
|
|
"a request served by an already-resident model must not look like a cold load")
|
|
})
|
|
|
|
It("does not repeat the effective runtime tuning banner at info level", func() {
|
|
_, err := ml.backendLoader(
|
|
WithModelID("resident-model"),
|
|
WithModel("resident-model"),
|
|
WithBackendString("llama-cpp"),
|
|
WithLoadGRPCLoadModelOpts(&pb.ModelOptions{ContextSize: 4096}),
|
|
)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
Expect(captured.String()).ToNot(ContainSubstring("effective runtime tuning"),
|
|
"the tuning banner documents what a load will run with, so it belongs to a load")
|
|
})
|
|
})
|
|
|
|
Context("when the model is not resident", func() {
|
|
It("still announces the cold load at info level", func() {
|
|
// The load itself fails (no such backend is installed); what is
|
|
// pinned here is that the banner is emitted before that, so
|
|
// suppressing the warm case does not silence real loads.
|
|
_, err := ml.backendLoader(
|
|
WithModelID("cold-model"),
|
|
WithModel("cold-model"),
|
|
WithBackendString("definitely-not-an-installed-backend"),
|
|
WithLoadGRPCLoadModelOpts(&pb.ModelOptions{ContextSize: 4096}),
|
|
)
|
|
Expect(err).To(HaveOccurred())
|
|
|
|
Expect(captured.String()).To(ContainSubstring("BackendLoader starting"))
|
|
Expect(captured.String()).To(ContainSubstring("effective runtime tuning"))
|
|
})
|
|
})
|
|
})
|