mirror of
https://github.com/mudler/LocalAI.git
synced 2026-10-28 09:08:54 -04:00
* fix(ci): capture and surface ginkgo errors in run-coverage.sh The coverage script ran ginkgo per test root with `|| fail=1`, which swallowed the exit code. Combined with `make --output-sync=target`, which buffers all output and flushes at the end, the actual ginkgo error was invisible in interleaved CI logs. Every test suite passed but the script still exited 1, and the error message was buried in 22 000 lines of buffered output. Each ginkgo invocation now captures combined stdout+stderr to a per-root log file. On failure the last 80 lines are printed to stderr so the error is visible in the CI log. On success the log is replayed to stdout so test output remains visible. The build flags (--tags, --coverpkg) are quoted correctly so values containing spaces (e.g. --tags="debug auth") are preserved through eval. Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(ci): create output dir before writing ginkgo logs The previous commit added per-root log files but forgot to create the output directory before the first ginkgo invocation. On CI the coverage/ dir did not exist yet, so the shell redirect to cover-pkg.out.log failed with "No such file or directory" before ginkgo even ran. Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * test(core/http): add canvas preview route to auth coverage allowlist Commit9ad78381d8added GET /api/canvas/preview/:id as a public route (no auth — the random id is the capability, an iframe cannot send API key headers) but did not add it to expectedPublicRoutes in the route auth coverage test. The test flagged the route as an anonymous-access leak, failing the entire ./core ginkgo suite on every CI run since. The POST /api/canvas/preview endpoint is auth-gated (returns 401 to anonymous requests) and is intentionally absent from the allowlist. Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(backends): prefer installed development builds Prefer compatible runnable development builds for generic aliases when the preference is enabled. Keep concrete names and disabled selection unchanged. Pass worker-local configuration into resolver state before registration. Synchronize saved frontend settings at startup and require a restart for resolver changes instead of switching registered paths during runtime. Add selection, root-shadowing, configuration, and restart-boundary tests. Document fallback behavior and worker-local activation. Assisted-by: Codex:gpt-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(settings): isolate backend restart notices A development preference save must not restart the watchdog. Separate its server restart notification from the legacy reconfiguration signal. Preserve existing response fields and genuine watchdog updates. Exercise the real settings endpoint for both preference values, repeated saves, and mixed watchdog updates. Verify resolver state and registered paths remain unchanged until startup. Attribution correction for commit85c46d2fac: its Assisted-by: Codex:gpt-5 trailer is incorrect. The actual agent was nib using model gpt-6.1-sol. Preserve that commit as task history. Assisted-by: nib:gpt-6.1-sol Signed-off-by: Ettore Di Giacinto <mudler@localai.io> --------- Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
75 lines
3.2 KiB
Go
75 lines
3.2 KiB
Go
// SPDX-License-Identifier: MIT
|
|
package application
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/mudler/LocalAI/core/config"
|
|
"github.com/mudler/LocalAI/core/gallery"
|
|
"github.com/mudler/LocalAI/pkg/model"
|
|
"github.com/mudler/LocalAI/pkg/system"
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
var _ = Describe("development backend preference", func() {
|
|
BeforeEach(func() {
|
|
GinkgoT().Setenv("LOCALAI_FORCE_META_BACKEND_CAPABILITY", "default")
|
|
})
|
|
installedState := func() *system.SystemState {
|
|
root := GinkgoT().TempDir()
|
|
for _, name := range []string{"cpu-example-engine", "cpu-example-engine-development"} {
|
|
dir := filepath.Join(root, name)
|
|
Expect(os.MkdirAll(dir, 0755)).To(Succeed())
|
|
data, err := json.Marshal(gallery.BackendMetadata{Name: name, Alias: "example-engine"})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(os.WriteFile(filepath.Join(dir, "metadata.json"), data, 0644)).To(Succeed())
|
|
Expect(os.WriteFile(filepath.Join(dir, "run.sh"), nil, 0644)).To(Succeed())
|
|
}
|
|
st, err := system.GetSystemState(system.WithBackendPath(root), system.WithBackendSystemPath(GinkgoT().TempDir()))
|
|
Expect(err).NotTo(HaveOccurred())
|
|
return st
|
|
}
|
|
It("synchronizes saved true at startup, including default-valued explicit false", func() {
|
|
dir := GinkgoT().TempDir()
|
|
Expect(os.WriteFile(filepath.Join(dir, "runtime_settings.json"), []byte(`{"prefer_development_backends":true}`), 0644)).To(Succeed())
|
|
st := installedState()
|
|
cfg := config.NewApplicationConfig(config.WithSystemState(st), config.WithPreferDevelopmentBackends(false))
|
|
cfg.DynamicConfigsDir = dir
|
|
loadStartupSettings(cfg)
|
|
Expect(cfg.PreferDevelopmentBackends).To(BeTrue())
|
|
Expect(st.PreferDevelopmentBackends).To(BeTrue())
|
|
ml := model.NewModelLoader(st)
|
|
Expect(gallery.RegisterBackends(st, ml)).To(Succeed())
|
|
path := ml.GetExternalBackend("example-engine")
|
|
Expect(path).To(HaveSuffix("cpu-example-engine-development/run.sh"))
|
|
})
|
|
It("preserves system-state-only true", func() {
|
|
st, err := system.GetSystemState(system.WithPreferDevelopmentBackends(true))
|
|
Expect(err).NotTo(HaveOccurred())
|
|
cfg := config.NewApplicationConfig(config.WithSystemState(st))
|
|
loadStartupSettings(cfg)
|
|
Expect(st.PreferDevelopmentBackends).To(BeTrue())
|
|
})
|
|
It("does not request watchdog reconfiguration or hot-switch API or file-watcher state", func() {
|
|
st := installedState()
|
|
cfg := config.NewApplicationConfig(config.WithSystemState(st))
|
|
ml := model.NewModelLoader(st)
|
|
Expect(gallery.RegisterBackends(st, ml)).To(Succeed())
|
|
original := ml.GetExternalBackend("example-engine")
|
|
Expect(original).To(HaveSuffix("cpu-example-engine/run.sh"))
|
|
yes := true
|
|
Expect(cfg.ApplyRuntimeSettings(&config.RuntimeSettings{PreferDevelopmentBackends: &yes})).To(BeFalse())
|
|
Expect(cfg.PreferDevelopmentBackends).To(BeTrue())
|
|
Expect(st.PreferDevelopmentBackends).To(BeFalse())
|
|
cfg.PreferDevelopmentBackends = false
|
|
Expect(readRuntimeSettingsJson(config.ApplicationConfig{})([]byte(`{"prefer_development_backends":true}`), cfg)).To(Succeed())
|
|
Expect(cfg.PreferDevelopmentBackends).To(BeTrue())
|
|
Expect(st.PreferDevelopmentBackends).To(BeFalse())
|
|
current := ml.GetExternalBackend("example-engine")
|
|
Expect(current).To(Equal(original))
|
|
})
|
|
})
|