Files
LocalAI/core/services/nodes/env_test.go
T
mudler-agentandEttore Di Giacinto 3a02f29edc fix(proxy): propagate model env and accept direct keys (#12940)
* feat(proxy): accept direct upstream API keys

Support direct credentials for models created in the UI. Keep env and
file references as mutually exclusive alternatives. Use model env for
frontend probes and omit credentials from structured config logs.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(distributed): isolate model environments in worker processes

Forward complete model environment snapshots through backend installation.
Restart worker children when overrides change or are removed, and include
keyed snapshot identities in frontend install coalescing.

Cover wire compatibility, routing, real worker children, and option copying;
document rollout requirements and control-transport security.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(worker): serialize installs by model replica

Different backend names can target the same process slot. Hold a slot
lock through startup, readiness, and operation attachment so concurrent
installs cannot reuse another configuration or an unready child.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(nodes): update endpoint stub for model env

Keep the endpoint command stub aligned with the environment-aware install
interface. Assert interface conformance so future changes fail at the
stub declaration rather than at endpoint call sites.

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>
2026-10-27 10:27:45 +01:00

61 lines
2.8 KiB
Go

// SPDX-License-Identifier: MIT
package nodes
import (
"context"
"github.com/mudler/LocalAI/core/services/workerctl"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"time"
)
var _ = Describe("Router install environment", func() {
It("keeps changed snapshots out of an in-flight identical slot", func() {
started := make(chan struct{}, 2)
release := make(chan struct{})
defer close(release)
unloader := &fakeUnloader{installReply: &workerctl.BackendInstallReply{Success: true, Address: "127.0.0.1:1234"}, installHook: func() { started <- struct{}{}; <-release }}
router := NewSmartRouter(&fakeModelRouter{}, SmartRouterOptions{Unloader: unloader})
done := make(chan error, 2)
for _, value := range []string{"first", "second"} {
go func(value string) {
_, err := router.installBackendOnNode(context.Background(), &BackendNode{ID: "node"}, "backend", "model", 0, map[string]string{"TOKEN": value})
done <- err
}(value)
}
Eventually(started).Should(Receive())
Eventually(started).Should(Receive())
// Both leaders entered before either could finish, so they cannot have shared
// one install reply (and therefore one child's credentials).
go func() { release <- struct{}{}; release <- struct{}{} }()
Eventually(done).Should(Receive(BeNil()))
Eventually(done).Should(Receive(BeNil()))
Expect(unloader.installCalls).To(HaveLen(2))
Expect([]string{unloader.installCalls[0].env["TOKEN"], unloader.installCalls[1].env["TOKEN"]}).To(ConsistOf("first", "second"))
})
It("coalesces identical snapshots without placing credentials in the identity", func() {
a := map[string]string{"TOKEN": "test-value", "EMPTY": ""}
b := map[string]string{"EMPTY": "", "TOKEN": "test-value"}
Expect(installEnvIdentity(a)).To(Equal(installEnvIdentity(b)))
Expect(installEnvIdentity(a)).NotTo(ContainSubstring("test-value"))
Expect(installEnvIdentity(nil)).NotTo(Equal(installEnvIdentity(map[string]string{})))
})
It("forwards complete and explicitly empty snapshots through the operation-aware fake", func() {
fake := &fakeOpWorker{fakeUnloader: &fakeUnloader{}}
for _, env := range []map[string]string{{"TOKEN": "test-value"}, {}} {
_, err := fake.InstallBackendOp("node", "backend", "model", "", 0, "", "operation", time.Minute, nil, env)
Expect(err).NotTo(HaveOccurred())
Expect(fake.installs[len(fake.installs)-1].EnvVars).To(Equal(env))
Expect(fake.installCalls[len(fake.installCalls)-1].env).To(Equal(env))
}
})
It("copies snapshots and preserves absence separately from removal", func() {
Expect(installEnvSnapshot(nil)).To(BeNil())
Expect(installEnvSnapshot([]map[string]string{nil})).NotTo(BeNil())
env := map[string]string{"TOKEN": "test-value"}
snapshot := installEnvSnapshot([]map[string]string{env})
env["TOKEN"] = "changed"
Expect(snapshot["TOKEN"]).To(Equal("test-value"))
})
})