mirror of
https://github.com/mudler/LocalAI.git
synced 2026-11-06 04:23:15 -05:00
An add-on can declare userSettings in its manifest: a key, the allowed
values and a default. The host keeps each person's answer in the account
database every frontend shares (one file when accounts are off), checks
every write against the declaration, and never asks the add-on.
GET and PUT /api/addons/{id}/user-settings read and write them. A write
needs the person's own browser session, so no API key, model or tool can
change one. A setting may carry a role that the host reads for its own
rules; the host fixes the values and the default of a role so a manifest
cannot loosen it. Pages reach their settings through sdk.userSettings,
which is bound to the add-on that calls it.
Assisted-by: Claude:claude-sonnet-5-5 go node
58 lines
2.1 KiB
Go
58 lines
2.1 KiB
Go
// SPDX-License-Identifier: MIT
|
|
package application
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/mudler/LocalAI/core/config"
|
|
"github.com/mudler/LocalAI/core/http/auth"
|
|
)
|
|
|
|
// The chats read the answer that the settings routes wrote. Without accounts
|
|
// there is one local owner: the routes write under it, so a chat of any name
|
|
// must read it.
|
|
func TestStudioUserSettingReadsWhatTheRoutesWrite(t *testing.T) {
|
|
a := &Application{applicationConfig: &config.ApplicationConfig{Context: context.Background(), DataPath: t.TempDir()}}
|
|
store, err := a.AddonUserSettingsStore()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ctx := context.Background()
|
|
if v, ok, err := a.studioUserSetting(ctx, "", "memory", "save-mode"); err != nil || ok || v != "" {
|
|
t.Fatalf("unset = %q, %v, %v", v, ok, err)
|
|
}
|
|
if err := store.Set(ctx, auth.AddonLocalOwnerID, "memory", "save-mode", "auto"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, user := range []string{"", "anonymous", "someone"} {
|
|
v, ok, err := a.studioUserSetting(ctx, user, "memory", "save-mode")
|
|
if err != nil || !ok || v != "auto" {
|
|
t.Fatalf("user %q reads %q, %v, %v", user, v, ok, err)
|
|
}
|
|
}
|
|
if _, ok, _ := a.studioUserSetting(ctx, "", "memory", "other-key"); ok {
|
|
t.Fatal("another key must not read the value")
|
|
}
|
|
if _, ok, _ := a.studioUserSetting(ctx, "", "other-addon", "save-mode"); ok {
|
|
t.Fatal("another add-on must not read the value")
|
|
}
|
|
}
|
|
|
|
func TestAddonUserSettingsStoreSurvivesARestart(t *testing.T) {
|
|
dir := t.TempDir()
|
|
first := &Application{applicationConfig: &config.ApplicationConfig{Context: context.Background(), DataPath: dir}}
|
|
s, err := first.AddonUserSettingsStore()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.Set(context.Background(), auth.AddonLocalOwnerID, "memory", "save-mode", "never"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
second := &Application{applicationConfig: &config.ApplicationConfig{Context: context.Background(), DataPath: dir}}
|
|
v, ok, err := second.studioUserSetting(context.Background(), "", "memory", "save-mode")
|
|
if err != nil || !ok || v != "never" {
|
|
t.Fatalf("after a restart = %q, %v, %v", v, ok, err)
|
|
}
|
|
}
|