mirror of
https://github.com/mudler/LocalAI.git
synced 2026-11-04 03:24:43 -05:00
* 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>
61 lines
2.8 KiB
Go
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"))
|
|
})
|
|
})
|