mirror of
https://github.com/mudler/LocalAI.git
synced 2026-11-06 04:23:15 -05:00
With authentication off every caller that reaches the port is the same local owner, and the checks that tell a person from a script are headers the caller chooses. Any process or agent that could reach the port could grant host operations, set deny rules and turn the blanket approval on. UI host grants are now locked by default in that mode. The policy file and flags still grant, and every write route answers 403 locked_by_deployment with a sentence that says why. The overview and the preview carry the lock and its reason for the page. The new flag --addon-host-ui-grants-without-auth turns the UI grants on knowingly and logs a startup warning. With sign-in on nothing changes. Assisted-by: Claude:claude-sonnet-5-5 go gofmt
1736 lines
60 KiB
Go
1736 lines
60 KiB
Go
package config
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"regexp"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/mudler/LocalAI/core/services/addons/gateway"
|
|
"github.com/mudler/LocalAI/core/services/addons/hostsettings"
|
|
"github.com/mudler/LocalAI/pkg/addons/contract"
|
|
"github.com/mudler/LocalAI/pkg/diagnostics"
|
|
"github.com/mudler/LocalAI/pkg/modelartifacts"
|
|
"github.com/mudler/LocalAI/pkg/system"
|
|
"github.com/mudler/LocalAI/pkg/vrambudget"
|
|
"github.com/mudler/LocalAI/pkg/xsysinfo"
|
|
"github.com/mudler/xlog"
|
|
)
|
|
|
|
// AddonConfig is startup-only deployment configuration. Authority is injected
|
|
// by the embedding host and cannot be loaded from runtime settings.
|
|
type AddonConfig struct {
|
|
Transport *gateway.CarrierConfig `json:"-" yaml:"-"`
|
|
ListenAddress string
|
|
|
|
// Frontend is set for the frontend role, also when add-ons are off. A
|
|
// frontend keeps no owner state, so it never opens the library store.
|
|
Frontend bool
|
|
// DedicatedOwner has no implicit legacy policy source. Migration must name
|
|
// an explicitly selected owner configuration; frontend state is never imported.
|
|
DedicatedOwner bool
|
|
// MigrateHostSettings selects only this owner process's effective legacy policy.
|
|
MigrateHostSettings bool
|
|
HostSettingsMigration *hostsettings.Migration `json:"-" yaml:"-"`
|
|
|
|
Enabled bool
|
|
InstanceID, OwnerID, OwnerAddress, StorageID, Root string
|
|
SharedStorage bool
|
|
// OwnershipMode is "named-user" (the default when empty) or "local-owner".
|
|
// Local-owner mode is never inferred from a request. The CLI sets it as the
|
|
// documented default when authentication is off and the operator chose no
|
|
// mode. Every caller that reaches the addon routes shares one authority and
|
|
// sharing is disabled.
|
|
// Every frontend of a deployment must use the same value.
|
|
OwnershipMode string
|
|
// OfficialLibrary registers the official add-on library on the first start,
|
|
// when the owner has no sources.json yet. A sources.json that exists is
|
|
// never changed by it, so a library the admin removed stays removed.
|
|
OfficialLibrary bool
|
|
// OfficialBackendLibrary also registers the backend library on the first
|
|
// start, under the same rule: a sources.json that exists is never changed,
|
|
// so a library the admin removed stays removed. It is on by default, but
|
|
// whether it is enabled depends on the build (core/library.BackendSource):
|
|
// a build that reads the backend gallery lists the library and leaves it
|
|
// off.
|
|
OfficialBackendLibrary bool
|
|
// BackendMaxBytes bounds the compressed size of one backend image
|
|
// installed from a library; zero is 8 GiB. Add-ons keep their own limit.
|
|
BackendMaxBytes int64
|
|
Host contract.HostCompatibility
|
|
AdminCheck func(context.Context, contract.Actor) error `json:"-" yaml:"-"`
|
|
// BackupKeep is how many backups of one add-on the owner keeps; zero is
|
|
// three. BackupMaxAge removes older ones; zero keeps them by count only.
|
|
// The newest verified backup and the recovery copy of an upgrade are
|
|
// never removed by either.
|
|
BackupKeep int
|
|
BackupMaxAge time.Duration
|
|
// UpdateCacheTTL is how long an answer of the update check, and the
|
|
// library data under it, counts as fresh; zero is 15 minutes.
|
|
// UpdateRefreshInterval is the least time between two explicit refreshes
|
|
// of one add-on and of one library; zero is 15 seconds.
|
|
UpdateCacheTTL time.Duration
|
|
UpdateRefreshInterval time.Duration
|
|
// ArtifactExpiry is how long a private file result stays downloadable;
|
|
// zero is the contract default of one hour. It is a programmatic option
|
|
// that no flag sets: the acceptance fixture shortens it to test expiry.
|
|
ArtifactExpiry time.Duration
|
|
}
|
|
|
|
// IsFrontend says whether this process is an add-on frontend: the role was set,
|
|
// or the instance is not the owner it forwards to.
|
|
func (c AddonConfig) IsFrontend() bool {
|
|
return c.Frontend || (c.Enabled && c.OwnerID != "" && c.InstanceID != c.OwnerID)
|
|
}
|
|
|
|
func WithAddons(c AddonConfig) AppOption {
|
|
return func(o *ApplicationConfig) { o.Addons = c }
|
|
}
|
|
|
|
type ApplicationConfig struct {
|
|
chatSelection *chatSelectionState
|
|
// OfficialLibraries is the switch for the shipped default library sources
|
|
// of every kind. On by default; off keeps them listed but unread.
|
|
OfficialLibraries bool
|
|
|
|
Addons AddonConfig `json:"-" yaml:"-"`
|
|
|
|
// AddonHostOps maps an add-on ID to the host operation prefixes it may
|
|
// call. It is startup-only. Nil or empty means no add-on gets a host
|
|
// operation endpoint.
|
|
AddonHostOps map[string][]string `json:"-" yaml:"-"`
|
|
// AddonHostOpsSource names where the startup grants came from: flag,
|
|
// environment, policy or empty. It never holds a path.
|
|
AddonHostOpsSource string `json:"-" yaml:"-"`
|
|
// AddonHostDeny maps an add-on ID, or "*" for all, to host operation
|
|
// prefixes, exact names or "*" that no grant may enable. Startup-only;
|
|
// deny set in the UI is stored separately and applies live.
|
|
AddonHostDeny map[string][]string `json:"-" yaml:"-"`
|
|
// AddonHostBlanket is the blanket approval mode set by the deployment
|
|
// ("off", "official", "any"). Empty leaves the choice to the UI.
|
|
AddonHostBlanket string `json:"-" yaml:"-"`
|
|
// AddonHostUIGrantsLocked makes the UI grant store read-only, so the
|
|
// policy file and flags are the only source of grants.
|
|
AddonHostUIGrantsLocked bool `json:"-" yaml:"-"`
|
|
// AddonHostUIGrantsWithoutAuth turns UI host grants on although
|
|
// authentication is off. Without it they are locked in that mode, since
|
|
// anyone who can reach the port could change them.
|
|
AddonHostUIGrantsWithoutAuth bool `json:"-" yaml:"-"`
|
|
|
|
// DiagnosticsRecorder is startup-only and local to this frontend process.
|
|
DiagnosticsRecorder *diagnostics.Recorder `json:"-" yaml:"-"`
|
|
|
|
// ProxyAPIKeyEnvLookup resolves upstream credentials at the CLI boundary.
|
|
ProxyAPIKeyEnvLookup func(string) string `json:"-" yaml:"-"`
|
|
|
|
Context context.Context
|
|
ConfigFile string
|
|
SystemState *system.SystemState
|
|
ExternalBackends []string
|
|
|
|
// ModelArtifactMaterializer is a controller-only acquisition capability.
|
|
// It is excluded from serialization so credentials captured by its concrete
|
|
// implementation cannot enter persisted settings or distributed payloads.
|
|
ModelArtifactMaterializer ArtifactMaterializer `json:"-" yaml:"-"`
|
|
ModelPreloadRenderMode string `json:"-" yaml:"-"`
|
|
DisableModelPreloadColor bool `json:"-" yaml:"-"`
|
|
|
|
// WebRTCNAT1To1IPs, when set, are advertised as the host ICE candidates for
|
|
// /v1/realtime WebRTC instead of every local interface address. Needed when
|
|
// the routable address differs from what pion gathers — e.g. Docker host
|
|
// networking (where pion also offers unreachable bridge IPs) or NAT.
|
|
WebRTCNAT1To1IPs []string
|
|
// WebRTCICEInterfaces, when set, restricts ICE candidate gathering to these
|
|
// network interfaces (e.g. eth0), filtering out docker0/veth noise.
|
|
WebRTCICEInterfaces []string
|
|
// WebRTCUDPPort, when positive, is the shared UDP port used by all WebRTC
|
|
// peer connections. Zero keeps pion's default ephemeral-port behavior.
|
|
WebRTCUDPPort int
|
|
UploadLimitMB, Threads, ContextSize int
|
|
ArtifactDownloadConcurrency int
|
|
|
|
// MaxConcurrentBackendOperations bounds how many backend install, upgrade
|
|
// and delete operations run at once. Always >= 1.
|
|
MaxConcurrentBackendOperations int
|
|
// liveMaxBackendOps mirrors MaxConcurrentBackendOperations for readers on
|
|
// other goroutines, since a runtime settings change can land at any time.
|
|
liveMaxBackendOps atomic.Int64
|
|
|
|
F16 bool
|
|
Debug bool
|
|
LogClient bool
|
|
TrustedProxies string // comma-separated IPs/CIDRs whose forwarding headers are believed
|
|
EnableTracing bool
|
|
MaxConcurrentBackendRequests int
|
|
TracingMaxItems int
|
|
TracingMaxBodyBytes int // Per-body cap for captured request/response bodies; 0 disables the cap
|
|
EnableBackendLogging bool
|
|
GeneratedContentDir string
|
|
|
|
UploadDir string
|
|
DataPath string // Persistent data directory for collectiondb, agents, etc.
|
|
|
|
DynamicConfigsDir string
|
|
DynamicConfigsDirPollInterval time.Duration
|
|
CORS bool
|
|
DisableCSRF bool
|
|
// DisableHTTPCompression turns off the gzip response middleware. Gzip is
|
|
// on by default because the React UI bundle and the admin JSON APIs are
|
|
// text-heavy; turn it off only when a fronting proxy already compresses.
|
|
DisableHTTPCompression bool
|
|
// HTTPCompressionMinLength is the response-size floor (bytes) below which
|
|
// gzip is skipped. 0 keeps middleware.DefaultCompressionMinLength.
|
|
HTTPCompressionMinLength int
|
|
PreloadJSONModels string
|
|
PreloadModelsFromPath string
|
|
CORSAllowOrigins string
|
|
ApiKeys []string
|
|
P2PEnableLimits bool
|
|
P2PToken string
|
|
P2PNetworkID string
|
|
Federated bool
|
|
|
|
// ExternalBaseURL is the externally visible base URL of this instance
|
|
// (scheme+host[:port]), set via LOCALAI_BASE_URL. When non-empty it is
|
|
// authoritative for every self-referential URL LocalAI emits (OAuth
|
|
// callbacks, generated image/video links, async job StatusURLs),
|
|
// overriding proxy-header detection. Empty = derive from request headers.
|
|
ExternalBaseURL string
|
|
|
|
// DisableStats turns off per-request token tracking. By default the
|
|
// routing module's billing recorder runs in every mode (including
|
|
// no-auth single-user) so dashboards and `/api/usage` are immediately
|
|
// useful; set this to opt out of that, e.g., for ephemeral CI runs
|
|
// or privacy-strict deployments where no token-count history should
|
|
// touch disk or memory.
|
|
DisableStats bool
|
|
|
|
// RouterDecisionLog retains the bounded in-memory routing log even when
|
|
// billing stats are disabled. It does not enable token usage recording.
|
|
RouterDecisionLog bool
|
|
|
|
// MITMListen is the address (host:port) the cloudproxy MITM
|
|
// listener binds on. Empty disables the MITM proxy entirely.
|
|
// Use case: redacting PII from Claude Code / Codex CLI traffic
|
|
// without LocalAI holding the upstream API key. Clients set
|
|
// HTTPS_PROXY=http://localai:port and trust the CA cert
|
|
// LocalAI exposes at /api/middleware/proxy-ca.crt.
|
|
MITMListen string
|
|
|
|
// PIIDefaultDetectors lists token-classification (NER) detector model
|
|
// names applied to any PII-enabled model that does not name its own
|
|
// pii.detectors. This makes cloud-proxy / MITM redaction work out of the
|
|
// box (those default to PII-enabled but carry no detector list) and lets
|
|
// an operator set one detector for the whole instance. Set at runtime via
|
|
// POST /api/settings; read live by Application.ResolvePIIPolicy.
|
|
PIIDefaultDetectors []string
|
|
|
|
// MITMCADir holds the persisted MITM proxy CA cert and private
|
|
// key. The CA is generated on first start; subsequent starts
|
|
// reload it so clients keep trusting the same root. The key
|
|
// file is mode 0600.
|
|
MITMCADir string
|
|
|
|
DisableWebUI bool
|
|
OllamaAPIRootEndpoint bool
|
|
EnforcePredownloadScans bool
|
|
OpaqueErrors bool
|
|
UseSubtleKeyComparison bool
|
|
DisableApiKeyRequirementForHttpGet bool
|
|
DisableMetrics bool
|
|
HttpGetExemptedEndpoints []*regexp.Regexp
|
|
DisableGalleryEndpoint bool
|
|
DisableMCP bool
|
|
LoadToMemory []string
|
|
|
|
Galleries []Gallery
|
|
BackendGalleries []Gallery
|
|
|
|
ExternalGRPCBackends map[string]string
|
|
|
|
AutoloadGalleries, AutoloadBackendGalleries bool
|
|
VRAMPersistentCache bool
|
|
AutoUpgradeBackends bool
|
|
AutoUpgradeAddons bool
|
|
PreferDevelopmentBackends bool
|
|
|
|
// RequireBackendIntegrity promotes a missing SHA256 (tarball/HTTP URIs)
|
|
// or missing verification policy (OCI URIs) from a warning to a hard
|
|
// failure during backend install/upgrade. Off by default to keep
|
|
// upgrades non-breaking; operators opt in explicitly via
|
|
// --require-backend-integrity / LOCALAI_REQUIRE_BACKEND_INTEGRITY.
|
|
RequireBackendIntegrity bool
|
|
|
|
// DisableBackendInstall mirrors SystemState.DisableBackendInstall for the
|
|
// HTTP layer: admission guards and the capability surfaces read it here.
|
|
DisableBackendInstall bool
|
|
|
|
SingleBackend bool // Deprecated: use MaxActiveBackends = 1 instead
|
|
MaxActiveBackends int // Maximum number of active backends (0 = unlimited, 1 = single backend mode)
|
|
WatchDogIdle bool
|
|
WatchDogBusy bool
|
|
WatchDog bool
|
|
|
|
// Memory Reclaimer settings (works with GPU if available, otherwise RAM)
|
|
MemoryReclaimerEnabled bool // Enable memory threshold monitoring
|
|
MemoryReclaimerThreshold float64 // Threshold 0.0-1.0 (e.g., 0.95 = 95%)
|
|
|
|
// VRAMBudget optionally caps how much VRAM this instance uses for model
|
|
// allocation, as "80%" or "12GB". Empty = use full detected VRAM.
|
|
VRAMBudget string
|
|
|
|
// Eviction settings
|
|
ForceEvictionWhenBusy bool // Force eviction even when models have active API calls (default: false for safety)
|
|
SizeAwareEviction bool // Evict largest models first rather than least-recently-used (default: false)
|
|
LRUEvictionMaxRetries int // Maximum number of retries when waiting for busy models to become idle (default: 30)
|
|
LRUEvictionRetryInterval time.Duration // Interval between retries when waiting for busy models (default: 1s)
|
|
|
|
// ModelLoadFailureCooldown is the base cooldown applied after a model load
|
|
// fails: new load attempts for that model are refused (HTTP 503 + Retry-After)
|
|
// until it elapses, doubling per consecutive failure up to a 5m cap and reset
|
|
// on success. Prevents a client polling a broken model from respawning a
|
|
// crashing backend on every request. 0 disables it. Default: 10s.
|
|
ModelLoadFailureCooldown time.Duration
|
|
|
|
ModelsURL []string
|
|
|
|
WatchDogBusyTimeout, WatchDogIdleTimeout time.Duration
|
|
WatchDogInterval time.Duration // Interval between watchdog checks
|
|
|
|
MachineTag string
|
|
|
|
APIAddress string
|
|
|
|
LlamaCPPTunnelCallback func(tunnels []string)
|
|
MLXTunnelCallback func(tunnels []string)
|
|
|
|
DisableRuntimeSettings bool
|
|
|
|
OpenResponsesStoreTTL time.Duration // TTL for Open Responses store (0 = no expiration)
|
|
|
|
PathWithoutAuth []string
|
|
|
|
// Collections (the knowledge base)
|
|
Collections CollectionsConfig
|
|
|
|
// ChatsVisionModel and ChatsTranscriptionModel are the models the chats'
|
|
// media tools use to describe images and transcribe audio. Empty: the first
|
|
// installed model with that capability.
|
|
ChatsVisionModel string
|
|
ChatsTranscriptionModel string
|
|
|
|
// Authentication & Authorization
|
|
Auth AuthConfig
|
|
|
|
// Distributed / Horizontal Scaling
|
|
Distributed DistributedConfig
|
|
|
|
// ExposeNodeHeader, when true, activates middleware.ExposeNodeHeader on
|
|
// the inference routes (OpenAI chat/completions/embeddings, Anthropic
|
|
// /v1/messages, Ollama /api/chat,/api/generate,/api/embed). The
|
|
// middleware wraps the response writer and attaches an "X-LocalAI-Node"
|
|
// response header carrying the ID of the distributed-mode worker node
|
|
// that served the request. Off by default because the node ID is
|
|
// internal topology that can aid attacker reconnaissance if surfaced on
|
|
// a public endpoint; operators opt in explicitly via
|
|
// --expose-node-header / LOCALAI_EXPOSE_NODE_HEADER for debugging,
|
|
// observability and load-balancer attribution.
|
|
ExposeNodeHeader bool
|
|
|
|
// LocalAI Assistant chat modality. Hard-disable the in-process admin MCP
|
|
// server with this flag; runtime-toggleable via /api/settings.
|
|
DisableLocalAIAssistant bool
|
|
|
|
// DisableSharedPages turns off the pages of shared folders: they are not
|
|
// listed as runnable, do not open and their bridge refuses every call.
|
|
// Runtime-toggleable via /api/settings (shared_pages_enabled).
|
|
DisableSharedPages bool
|
|
|
|
// ChatsHostExec is who gets the chats' built-in bash, which runs commands
|
|
// on this machine with no isolation: off, ask (administrators, with a card
|
|
// for each command) or on. Empty: off with accounts, ask without.
|
|
// Runtime-toggleable via /api/settings (chats_host_exec).
|
|
ChatsHostExec string
|
|
// ChatsToolProviders pins, for a built-in chat tool or skill that several
|
|
// add-ons replace, the add-on that serves it (tool or skill name -> add-on
|
|
// ID). Tool and skill names never overlap.
|
|
ChatsToolProviders map[string]string
|
|
|
|
// DisableChats turns off the whole chats surface: the /api/chats API, saved
|
|
// agents, triggers, connectors and the agent tasks and jobs. Nothing starts
|
|
// (no loop, bot or trigger runner).
|
|
DisableChats bool
|
|
// DisableTriggers and DisableConnectors turn off one part of the chats:
|
|
// the trigger runner, or the connector bots. The API for the part answers
|
|
// 404.
|
|
DisableTriggers bool
|
|
DisableConnectors bool
|
|
// DisableConnectorTalkback hides the tool with which an agent writes to a
|
|
// chat of its connection and stops the reports of triggers to a channel.
|
|
// Owner notices keep working.
|
|
DisableConnectorTalkback bool
|
|
// SkipBindingCapsMigration keeps the one-time grant of the usual
|
|
// permissions to old connector bindings from running.
|
|
SkipBindingCapsMigration bool
|
|
// AgentJobRetentionDays is how long finished agent jobs are kept. Zero
|
|
// means DefaultAgentJobRetentionDays.
|
|
AgentJobRetentionDays int
|
|
// MCPCIJobTimeout is the longest an agent job may run. Zero means
|
|
// DefaultMCPCIJobTimeout.
|
|
MCPCIJobTimeout time.Duration
|
|
// TriggerRunKeep is the number of finished runs kept for each trigger
|
|
// (beyond the last two days). Zero means triggers.DefaultKeepRuns, a negative
|
|
// value keeps every run.
|
|
TriggerRunKeep int
|
|
// TriggerRunRetentionDays is how long finished trigger runs are kept. Zero
|
|
// means 30 days (triggers.DefaultRunMaxAge), a negative value keeps them forever.
|
|
TriggerRunRetentionDays int
|
|
|
|
// MCPConnectorsAllowPrivate lets the MCP connectors that users define reach
|
|
// addresses on private networks (loopback, link-local, RFC 1918, metadata).
|
|
// Off by default: such an address lets a user probe the internal network
|
|
// through the server. Runtime-toggleable via /api/settings.
|
|
MCPConnectorsAllowPrivate bool
|
|
|
|
// The built-in web tools of the chats. Each is runtime-toggleable via
|
|
// /api/settings and read at each turn. DisableChatsWebSearch and
|
|
// DisableChatsWebFetch are stored as the negation (chats_web_search,
|
|
// chats_web_fetch), so both tools are on by default. ChatsWebSearXNGURL
|
|
// is the address of a SearXNG instance, used alone unless
|
|
// ChatsWebSearchProviders lists other providers.
|
|
// ChatsWebAllowPrivate lets web_fetch reach addresses on private networks
|
|
// (never the cloud metadata addresses). ChatsWebPorts is a comma list of
|
|
// the ports web_fetch may use; empty means 80 and 443.
|
|
DisableChatsWebSearch bool
|
|
DisableChatsWebFetch bool
|
|
ChatsWebSearXNGURL string
|
|
// ChatsWebSearchProviders is the order of the search providers of
|
|
// web_search, comma separated (see ParseSearchProviders). Empty means
|
|
// the default order.
|
|
ChatsWebSearchProviders string
|
|
ChatsWebAllowPrivate bool
|
|
ChatsWebPorts string
|
|
|
|
// The network of the commands that chats run in the Sandbox, through the
|
|
// host proxy. Each is runtime-toggleable via /api/settings and read at
|
|
// each check, so no restart. ChatsEgressCeiling is the widest preset
|
|
// (off, registries, all) any chat may use; ChatsEgressDefault the preset
|
|
// of a new chat, capped by the ceiling. ChatsEgressAllowPrivate lets an
|
|
// administrator's rule or card answer reach private addresses (never the
|
|
// metadata addresses). ChatsEgressPorts is a comma list of the ports a
|
|
// command may reach (empty: 80 and 443), ChatsEgressExtraRegistries a
|
|
// comma list of host names added to the package registries, and
|
|
// ChatsEgressAllowedHosts exact hosts (host or host:port, comma separated)
|
|
// that any chat with the network on may always reach;
|
|
// ChatsEgressAuditDays how long the audit keeps a line (0: 30 days).
|
|
ChatsEgressCeiling string
|
|
ChatsEgressDefault string
|
|
ChatsEgressAllowPrivate bool
|
|
ChatsEgressPorts string
|
|
ChatsEgressExtraRegistries string
|
|
ChatsEgressAllowedHosts string
|
|
ChatsEgressAuditDays int
|
|
|
|
// chatsLimitsMu guards ChatsEgressAuditDays and the chats_loop_* limits.
|
|
// Background goroutines (the audit prune, the loop runner) read them
|
|
// while a settings POST rewrites them; use the Effective getters and the
|
|
// registry setters, which hold it.
|
|
chatsLimitsMu sync.RWMutex
|
|
|
|
// ChatsMaxIterations is the step limit of the tool loop of one chat turn:
|
|
// how many rounds of tool calls the model may make before it must answer.
|
|
// A turn that reaches it ends as paused, and the person can let it go on.
|
|
// 0: DefaultChatsMaxIterations. A model's agent.max_iterations wins.
|
|
// Runtime-settable via /api/settings (chats_max_iterations).
|
|
ChatsMaxIterations int
|
|
|
|
// ChatsGoalMaxTurns is the most turns the server starts by itself for one
|
|
// goal of a chat, and ChatsGoalMaxSteps the most model requests the turns
|
|
// of one goal make, before the goal pauses. 0: the defaults. Runtime-settable
|
|
// via /api/settings (chats_goal_max_turns, chats_goal_max_steps).
|
|
ChatsGoalMaxTurns int
|
|
ChatsGoalMaxSteps int
|
|
// The limits of the loops of the chats (a prompt a chat repeats by
|
|
// itself). Zero means the default. Runtime-settable via /api/settings
|
|
// (chats_loop_min_interval, chats_loop_max_runs, chats_loop_max_per_chat,
|
|
// chats_loop_max_per_user, chats_loop_daily_runs); read at each use.
|
|
ChatsLoopMinInterval time.Duration
|
|
ChatsLoopMaxRuns int
|
|
ChatsLoopMaxPerChat int
|
|
ChatsLoopMaxPerUser int
|
|
ChatsLoopDailyRuns int
|
|
|
|
// DisableChatsSavedAgents switches off, for every chat, the saved agents
|
|
// as sub-agents (list_agents and the agent argument of spawn_agent).
|
|
// Stored as the negation (chats_saved_agents), so it is on by default;
|
|
// each chat still has to switch it on. Read at each turn.
|
|
DisableChatsSavedAgents bool
|
|
|
|
// DisableChatsWorkFolder stops the steering of the chats' working files:
|
|
// the prompt no longer names the chat's work folder (Chats/<chat>/work)
|
|
// for scripts, logs and drafts, and a helper file saved elsewhere gets no
|
|
// nudge. Stored as the negation (chats_work_folder), so it is on by
|
|
// default. Read at each turn.
|
|
DisableChatsWorkFolder bool
|
|
|
|
// DisableChatsIndexFiles stops the reading of the text of PDF, Word,
|
|
// Excel and PowerPoint files in the chats' files, which search_files and
|
|
// the Files page search. Stored as the negation (chats_index_files), so
|
|
// it is on by default. ChatsIndexMaxTextBytes is the most text kept for
|
|
// one file (chats_index_max_text_bytes; 0: 2 MiB). Both are read at each
|
|
// pass of the indexer, so no restart.
|
|
DisableChatsIndexFiles bool
|
|
ChatsIndexMaxTextBytes int
|
|
|
|
// DisableChatSuggestions stops the assistant from offering things (to
|
|
// remember a name, to make a shortcut, to update a widget on a schedule)
|
|
// to users who did not choose for themselves. Runtime-toggleable via
|
|
// /api/settings as chats_suggestions (stored as the negation).
|
|
DisableChatSuggestions bool
|
|
|
|
// LegacyNavigation shows the previous sidebar. See WithLegacyNavigation.
|
|
LegacyNavigation bool
|
|
|
|
// Branding / whitelabeling — runtime-mutable via /api/settings (text) and
|
|
// /api/branding/asset/:kind (binary uploads). All values optional; empty
|
|
// strings fall back to bundled LocalAI defaults.
|
|
Branding BrandingConfig
|
|
}
|
|
|
|
const DefaultMaxConcurrentBackendRequests = 1024
|
|
|
|
// BrandingConfig holds the whitelabel/branding configuration of the instance.
|
|
// Text fields are exposed via the public GET /api/branding endpoint so the
|
|
// login page can read them before authentication. Binary asset filenames
|
|
// (logo, horizontal logo, favicon) are stored as basenames; the actual files
|
|
// live under {DynamicConfigsDir}/branding/.
|
|
type BrandingConfig struct {
|
|
InstanceName string
|
|
InstanceTagline string
|
|
LogoFile string
|
|
LogoHorizontalFile string
|
|
FaviconFile string
|
|
}
|
|
|
|
// AuthConfig holds configuration for user authentication and authorization.
|
|
type AuthConfig struct {
|
|
Enabled bool
|
|
DatabaseURL string // "postgres://..." or file path for SQLite
|
|
GitHubClientID string
|
|
GitHubClientSecret string
|
|
OIDCIssuer string // OIDC issuer URL for auto-discovery (e.g. https://accounts.google.com)
|
|
OIDCClientID string
|
|
OIDCClientSecret string
|
|
AdminEmail string // auto-promote to admin on login
|
|
RegistrationMode string // "open", "approval" (default when empty), "invite"
|
|
DisableLocalAuth bool // disable local email/password registration and login
|
|
APIKeyHMACSecret string // HMAC secret for API key hashing; auto-generated if empty
|
|
DefaultAPIKeyExpiry string // default expiry duration for API keys (e.g. "90d"); empty = no expiry
|
|
}
|
|
|
|
// CollectionsConfig holds the configuration of the collections service.
|
|
type CollectionsConfig struct {
|
|
Enabled bool // default: true (disabled by LOCALAI_DISABLE_COLLECTIONS=true)
|
|
StateDir string // default: DynamicConfigsDir (LocalAI configuration folder)
|
|
APIURL string // default: self-referencing LocalAI (http://127.0.0.1:<port>)
|
|
APIKey string // default: first API key from LocalAI config
|
|
DefaultModel string
|
|
CollectionDBPath string
|
|
VectorEngine string // default: "chromem"
|
|
EmbeddingModel string // default: "granite-embedding-107m-multilingual"
|
|
MaxChunkingSize int // default: 400
|
|
ChunkOverlap int // default: 0
|
|
DatabaseURL string
|
|
}
|
|
|
|
type AppOption func(*ApplicationConfig)
|
|
|
|
func WithProxyAPIKeyEnvLookup(lookup func(string) string) AppOption {
|
|
return func(o *ApplicationConfig) { o.ProxyAPIKeyEnvLookup = lookup }
|
|
}
|
|
|
|
func NewApplicationConfig(o ...AppOption) *ApplicationConfig {
|
|
opt := &ApplicationConfig{
|
|
Context: context.Background(),
|
|
Debug: true,
|
|
ModelArtifactMaterializer: modelartifacts.NewDefaultManager(),
|
|
OfficialLibraries: true,
|
|
ModelPreloadRenderMode: "dark",
|
|
// Capture backend process stdout/stderr into the per-model
|
|
// BackendLogStore by default so the UI "Backend Logs" page works out
|
|
// of the box in single mode, matching worker/distributed mode (which
|
|
// force-enables it). It's a small in-memory ring buffer; the Settings
|
|
// toggle can still turn it off (a persisted false wins - see
|
|
// loadRuntimeSettingsFromFile).
|
|
EnableBackendLogging: true,
|
|
VRAMPersistentCache: true,
|
|
ArtifactDownloadConcurrency: modelartifacts.DefaultDownloadConcurrency,
|
|
|
|
MaxConcurrentBackendOperations: 1,
|
|
|
|
LRUEvictionMaxRetries: 30, // Default: 30 retries
|
|
LRUEvictionRetryInterval: 1 * time.Second, // Default: 1 second
|
|
ModelLoadFailureCooldown: 10 * time.Second, // Default: 10s base cooldown after a failed load
|
|
// WatchDogInterval is intentionally left at the zero value here.
|
|
// The startup loader applies a persisted runtime_settings.json value
|
|
// only when the interval is still 0 (its "not set by env var"
|
|
// heuristic, matching the idle/busy timeouts); a non-zero baseline
|
|
// default would defeat that and silently revert a UI-saved Check
|
|
// Interval to the default on every restart (#10601). The effective
|
|
// 500ms default is supplied at the watchdog layer (DefaultWatchdogInterval)
|
|
// when the value is still 0.
|
|
MaxConcurrentBackendRequests: DefaultMaxConcurrentBackendRequests,
|
|
TracingMaxItems: 1024,
|
|
TracingMaxBodyBytes: 64 * 1024, // 64 KiB - caps each request/response body in the trace buffer
|
|
Collections: CollectionsConfig{
|
|
Enabled: true,
|
|
VectorEngine: "chromem",
|
|
EmbeddingModel: "granite-embedding-107m-multilingual",
|
|
MaxChunkingSize: 400,
|
|
},
|
|
PathWithoutAuth: []string{},
|
|
}
|
|
for _, oo := range o {
|
|
oo(opt)
|
|
}
|
|
opt.SetMaxConcurrentBackendOperations(opt.MaxConcurrentBackendOperations)
|
|
opt.selectionState()
|
|
return opt
|
|
}
|
|
|
|
func WithModelsURL(urls ...string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.ModelsURL = urls
|
|
}
|
|
}
|
|
|
|
func WithSystemState(state *system.SystemState) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.SystemState = state
|
|
}
|
|
}
|
|
|
|
func WithExternalBackends(backends ...string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.ExternalBackends = backends
|
|
}
|
|
}
|
|
|
|
func WithWebRTCNAT1To1IPs(ips ...string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.WebRTCNAT1To1IPs = ips
|
|
}
|
|
}
|
|
|
|
func WithWebRTCICEInterfaces(interfaces ...string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.WebRTCICEInterfaces = interfaces
|
|
}
|
|
}
|
|
|
|
func WithWebRTCUDPPort(port int) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.WebRTCUDPPort = port
|
|
}
|
|
}
|
|
|
|
func WithMachineTag(tag string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.MachineTag = tag
|
|
}
|
|
}
|
|
|
|
func WithCors(b bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.CORS = b
|
|
}
|
|
}
|
|
|
|
func WithP2PNetworkID(s string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.P2PNetworkID = s
|
|
}
|
|
}
|
|
|
|
func WithDisableCSRF(b bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.DisableCSRF = b
|
|
}
|
|
}
|
|
|
|
func WithDisableHTTPCompression(b bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.DisableHTTPCompression = b
|
|
}
|
|
}
|
|
|
|
func WithHTTPCompressionMinLength(n int) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.HTTPCompressionMinLength = n
|
|
}
|
|
}
|
|
|
|
func WithP2PToken(s string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.P2PToken = s
|
|
}
|
|
}
|
|
|
|
var EnableWatchDog = func(o *ApplicationConfig) {
|
|
o.WatchDog = true
|
|
}
|
|
|
|
var EnableTracing = func(o *ApplicationConfig) {
|
|
o.EnableTracing = true
|
|
}
|
|
|
|
var EnableBackendLogging = func(o *ApplicationConfig) {
|
|
o.EnableBackendLogging = true
|
|
}
|
|
|
|
var EnableWatchDogIdleCheck = func(o *ApplicationConfig) {
|
|
o.WatchDog = true
|
|
o.WatchDogIdle = true
|
|
}
|
|
|
|
var DisableGalleryEndpoint = func(o *ApplicationConfig) {
|
|
o.DisableGalleryEndpoint = true
|
|
}
|
|
|
|
var DisableMCP = func(o *ApplicationConfig) {
|
|
o.DisableMCP = true
|
|
}
|
|
|
|
var EnableWatchDogBusyCheck = func(o *ApplicationConfig) {
|
|
o.WatchDog = true
|
|
o.WatchDogBusy = true
|
|
}
|
|
|
|
var DisableWebUI = func(o *ApplicationConfig) {
|
|
o.DisableWebUI = true
|
|
}
|
|
|
|
var EnableOllamaAPIRootEndpoint = func(o *ApplicationConfig) {
|
|
o.OllamaAPIRootEndpoint = true
|
|
}
|
|
|
|
var DisableRuntimeSettings = func(o *ApplicationConfig) {
|
|
o.DisableRuntimeSettings = true
|
|
}
|
|
|
|
func SetWatchDogBusyTimeout(t time.Duration) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.WatchDogBusyTimeout = t
|
|
}
|
|
}
|
|
|
|
func SetWatchDogIdleTimeout(t time.Duration) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.WatchDogIdleTimeout = t
|
|
}
|
|
}
|
|
|
|
func SetWatchDogInterval(t time.Duration) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.WatchDogInterval = t
|
|
}
|
|
}
|
|
|
|
// EnableMemoryReclaimer enables memory threshold monitoring.
|
|
// When enabled, the watchdog will evict backends if memory usage exceeds the threshold.
|
|
// Works with GPU VRAM if available, otherwise uses system RAM.
|
|
var EnableMemoryReclaimer = func(o *ApplicationConfig) {
|
|
o.MemoryReclaimerEnabled = true
|
|
o.WatchDog = true // Memory reclaimer requires watchdog infrastructure
|
|
}
|
|
|
|
// SetMemoryReclaimerThreshold sets the memory usage threshold (0.0-1.0).
|
|
// When memory usage exceeds this threshold, backends will be evicted using LRU strategy.
|
|
func SetMemoryReclaimerThreshold(threshold float64) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
if threshold > 0 && threshold <= 1.0 {
|
|
o.MemoryReclaimerThreshold = threshold
|
|
o.MemoryReclaimerEnabled = true
|
|
o.WatchDog = true // Memory reclaimer requires watchdog infrastructure
|
|
}
|
|
}
|
|
}
|
|
|
|
// SetVRAMBudget sets the VRAM allocation cap ("80%" or "12GB", "" = no cap).
|
|
func SetVRAMBudget(v string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.VRAMBudget = v
|
|
}
|
|
}
|
|
|
|
// WithMemoryReclaimer configures the memory reclaimer with the given settings
|
|
func WithMemoryReclaimer(enabled bool, threshold float64) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.MemoryReclaimerEnabled = enabled
|
|
if threshold > 0 && threshold <= 1.0 {
|
|
o.MemoryReclaimerThreshold = threshold
|
|
}
|
|
if enabled {
|
|
o.WatchDog = true // Memory reclaimer requires watchdog infrastructure
|
|
}
|
|
}
|
|
}
|
|
|
|
// EnableSingleBackend is deprecated: use SetMaxActiveBackends(1) instead.
|
|
// This is kept for backward compatibility.
|
|
var EnableSingleBackend = func(o *ApplicationConfig) {
|
|
o.SingleBackend = true
|
|
o.MaxActiveBackends = 1
|
|
}
|
|
|
|
// SetMaxActiveBackends sets the maximum number of active backends.
|
|
// 0 = unlimited, 1 = single backend mode (replaces EnableSingleBackend)
|
|
func SetMaxActiveBackends(n int) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.MaxActiveBackends = n
|
|
// For backward compatibility, also set SingleBackend if n == 1
|
|
if n == 1 {
|
|
o.SingleBackend = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// GetEffectiveMaxActiveBackends returns the effective max active backends limit.
|
|
// It considers both MaxActiveBackends and the deprecated SingleBackend setting.
|
|
// If MaxActiveBackends is set (> 0), it takes precedence.
|
|
// If SingleBackend is true and MaxActiveBackends is 0, returns 1.
|
|
// Otherwise returns 0 (unlimited).
|
|
func (o *ApplicationConfig) GetEffectiveMaxActiveBackends() int {
|
|
if o.MaxActiveBackends > 0 {
|
|
return o.MaxActiveBackends
|
|
}
|
|
if o.SingleBackend {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// WatchdogShouldRun reports whether the live watchdog process should be
|
|
// running for the current config. It mirrors the gating in
|
|
// (*Application).startWatchdog so the /api/settings start/stop decision and
|
|
// the startup path agree on a single source of truth: the watchdog runs when
|
|
// idle/busy checks are enabled (WatchDog), when LRU eviction is active
|
|
// (effective max active backends > 0), or when the memory reclaimer is on.
|
|
func (o *ApplicationConfig) WatchdogShouldRun() bool {
|
|
return o.WatchDog || o.GetEffectiveMaxActiveBackends() > 0 || o.MemoryReclaimerEnabled
|
|
}
|
|
|
|
// WithForceEvictionWhenBusy sets whether to force eviction even when models have active API calls
|
|
func WithForceEvictionWhenBusy(enabled bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.ForceEvictionWhenBusy = enabled
|
|
}
|
|
}
|
|
|
|
// WithSizeAwareEviction enables size-aware eviction ordering.
|
|
// When true, the watchdog evicts the largest loaded model first rather than the
|
|
// least-recently-used one, keeping small utility models resident and maximizing
|
|
// memory freed per eviction.
|
|
func WithSizeAwareEviction(enabled bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.SizeAwareEviction = enabled
|
|
}
|
|
}
|
|
|
|
// WithLRUEvictionMaxRetries sets the maximum number of retries when waiting for busy models to become idle
|
|
func WithLRUEvictionMaxRetries(maxRetries int) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
if maxRetries > 0 {
|
|
o.LRUEvictionMaxRetries = maxRetries
|
|
}
|
|
}
|
|
}
|
|
|
|
// WithLRUEvictionRetryInterval sets the interval between retries when waiting for busy models
|
|
func WithLRUEvictionRetryInterval(interval time.Duration) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
if interval > 0 {
|
|
o.LRUEvictionRetryInterval = interval
|
|
}
|
|
}
|
|
}
|
|
|
|
// WithModelLoadFailureCooldown sets the base cooldown applied after a failed
|
|
// model load. 0 disables the cooldown, so unlike most options it accepts any
|
|
// non-negative value.
|
|
func WithModelLoadFailureCooldown(cooldown time.Duration) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
if cooldown >= 0 {
|
|
o.ModelLoadFailureCooldown = cooldown
|
|
}
|
|
}
|
|
}
|
|
|
|
var EnableGalleriesAutoload = func(o *ApplicationConfig) {
|
|
o.AutoloadGalleries = true
|
|
}
|
|
|
|
var EnableBackendGalleriesAutoload = func(o *ApplicationConfig) {
|
|
o.AutoloadBackendGalleries = true
|
|
}
|
|
|
|
func WithOfficialLibraries(v bool) AppOption {
|
|
return func(o *ApplicationConfig) { o.OfficialLibraries = v }
|
|
}
|
|
|
|
func WithAutoUpgradeBackends(v bool) AppOption {
|
|
return func(o *ApplicationConfig) { o.AutoUpgradeBackends = v }
|
|
}
|
|
|
|
func WithAutoUpgradeAddons(v bool) AppOption {
|
|
return func(o *ApplicationConfig) { o.AutoUpgradeAddons = v }
|
|
}
|
|
|
|
func WithVRAMPersistentCache(v bool) AppOption {
|
|
return func(o *ApplicationConfig) { o.VRAMPersistentCache = v }
|
|
}
|
|
|
|
func WithRequireBackendIntegrity(v bool) AppOption {
|
|
return func(o *ApplicationConfig) { o.RequireBackendIntegrity = v }
|
|
}
|
|
|
|
func WithDisableBackendInstall(v bool) AppOption {
|
|
return func(o *ApplicationConfig) { o.DisableBackendInstall = v }
|
|
}
|
|
|
|
func WithPreferDevelopmentBackends(v bool) AppOption {
|
|
return func(o *ApplicationConfig) { o.PreferDevelopmentBackends = v }
|
|
}
|
|
|
|
var EnableFederated = func(o *ApplicationConfig) {
|
|
o.Federated = true
|
|
}
|
|
|
|
func WithExternalBackend(name string, uri string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
if o.ExternalGRPCBackends == nil {
|
|
o.ExternalGRPCBackends = make(map[string]string)
|
|
}
|
|
o.ExternalGRPCBackends[name] = uri
|
|
}
|
|
}
|
|
|
|
func WithCorsAllowOrigins(b string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.CORSAllowOrigins = b
|
|
}
|
|
}
|
|
|
|
func WithStringGalleries(galls string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
if galls == "" {
|
|
o.Galleries = []Gallery{}
|
|
return
|
|
}
|
|
var galleries []Gallery
|
|
if err := json.Unmarshal([]byte(galls), &galleries); err != nil {
|
|
xlog.Error("failed loading galleries", "error", err)
|
|
}
|
|
o.Galleries = append(o.Galleries, galleries...)
|
|
}
|
|
}
|
|
|
|
func WithBackendGalleries(galls string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
if galls == "" {
|
|
o.BackendGalleries = []Gallery{}
|
|
return
|
|
}
|
|
var galleries []Gallery
|
|
if err := json.Unmarshal([]byte(galls), &galleries); err != nil {
|
|
xlog.Error("failed loading galleries", "error", err)
|
|
}
|
|
o.BackendGalleries = append(o.BackendGalleries, galleries...)
|
|
}
|
|
}
|
|
|
|
func WithGalleries(galleries []Gallery) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Galleries = append(o.Galleries, galleries...)
|
|
}
|
|
}
|
|
|
|
func WithContext(ctx context.Context) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Context = ctx
|
|
}
|
|
}
|
|
|
|
// WithModelArtifactMaterializer injects the controller-only model acquisition capability.
|
|
func WithModelArtifactMaterializer(materializer ArtifactMaterializer) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
if materializer != nil {
|
|
o.ModelArtifactMaterializer = materializer
|
|
}
|
|
}
|
|
}
|
|
|
|
// WithMaxConcurrentBackendOperations sets how many backend operations run at
|
|
// once. Values below 1 fall back to 1.
|
|
func WithMaxConcurrentBackendOperations(n int) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
if n < 1 {
|
|
n = 1
|
|
}
|
|
o.SetMaxConcurrentBackendOperations(n)
|
|
}
|
|
}
|
|
|
|
// SetMaxConcurrentBackendOperations stores the limit, clamped to at least 1.
|
|
func (o *ApplicationConfig) SetMaxConcurrentBackendOperations(n int) {
|
|
if n < 1 {
|
|
n = 1
|
|
}
|
|
o.MaxConcurrentBackendOperations = n
|
|
o.liveMaxBackendOps.Store(int64(n))
|
|
}
|
|
|
|
// BackendOperationLimit returns how many backend operations may run at once.
|
|
// It is safe to call while a runtime settings change is applied.
|
|
func (o *ApplicationConfig) BackendOperationLimit() int {
|
|
if n := o.liveMaxBackendOps.Load(); n >= 1 {
|
|
return int(n)
|
|
}
|
|
if o.MaxConcurrentBackendOperations >= 1 {
|
|
return o.MaxConcurrentBackendOperations
|
|
}
|
|
return 1
|
|
}
|
|
|
|
func WithArtifactDownloadConcurrency(concurrency int) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
if concurrency < 1 {
|
|
concurrency = modelartifacts.DefaultDownloadConcurrency
|
|
}
|
|
o.ArtifactDownloadConcurrency = concurrency
|
|
}
|
|
}
|
|
|
|
// WithModelPreloadDisplay configures terminal rendering for model preload output.
|
|
func WithModelPreloadDisplay(renderMode string, disableColor bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
if renderMode != "" {
|
|
o.ModelPreloadRenderMode = renderMode
|
|
}
|
|
o.DisableModelPreloadColor = disableColor
|
|
}
|
|
}
|
|
|
|
func WithYAMLConfigPreload(configFile string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.PreloadModelsFromPath = configFile
|
|
}
|
|
}
|
|
|
|
func WithJSONStringPreload(configFile string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.PreloadJSONModels = configFile
|
|
}
|
|
}
|
|
|
|
func WithConfigFile(configFile string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.ConfigFile = configFile
|
|
}
|
|
}
|
|
|
|
// DefaultUploadLimitMB is the body limit, in MB, of every route that has no
|
|
// limit of its own, when the operator sets none.
|
|
const DefaultUploadLimitMB = 15
|
|
|
|
// UploadLimitSet reports whether the operator chose an upload limit. Zero
|
|
// means "not set", so a route with a larger limit of its own can keep it.
|
|
func (o *ApplicationConfig) UploadLimitSet() bool { return o.UploadLimitMB > 0 }
|
|
|
|
// GeneralUploadLimitMB is the body limit, in MB, of the routes that have no
|
|
// limit of their own: the operator's value, or the default.
|
|
func (o *ApplicationConfig) GeneralUploadLimitMB() int {
|
|
if o.UploadLimitSet() {
|
|
return o.UploadLimitMB
|
|
}
|
|
return DefaultUploadLimitMB
|
|
}
|
|
|
|
func WithUploadLimitMB(limit int) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.UploadLimitMB = limit
|
|
}
|
|
}
|
|
|
|
// WithThreads sets the thread-count hint. 0 means "unset": the effective
|
|
// physical-core default is resolved in application.New() only after
|
|
// persisted runtime settings merge, so a file-saved value is not masked by
|
|
// an eager fallback (#10845).
|
|
func WithThreads(threads int) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Threads = threads
|
|
}
|
|
}
|
|
|
|
func WithContextSize(ctxSize int) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.ContextSize = ctxSize
|
|
}
|
|
}
|
|
|
|
func WithLlamaCPPTunnelCallback(callback func(tunnels []string)) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.LlamaCPPTunnelCallback = callback
|
|
}
|
|
}
|
|
|
|
func WithMLXTunnelCallback(callback func(tunnels []string)) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.MLXTunnelCallback = callback
|
|
}
|
|
}
|
|
|
|
func WithF16(f16 bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.F16 = f16
|
|
}
|
|
}
|
|
|
|
func WithDebug(debug bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Debug = debug
|
|
}
|
|
}
|
|
|
|
// WithTrustedProxies sets the proxies whose X-Forwarded-For is believed.
|
|
func WithTrustedProxies(list string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.TrustedProxies = list
|
|
}
|
|
}
|
|
|
|
// WithLogClient adds the peer address and user agent to the HTTP access log.
|
|
func WithLogClient(on bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.LogClient = on
|
|
}
|
|
}
|
|
|
|
func WithTracingMaxItems(items int) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.TracingMaxItems = items
|
|
}
|
|
}
|
|
|
|
func WithMaxConcurrentBackendRequests(requests int) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
if requests <= 0 {
|
|
requests = DefaultMaxConcurrentBackendRequests
|
|
}
|
|
o.MaxConcurrentBackendRequests = requests
|
|
}
|
|
}
|
|
|
|
func WithTracingMaxBodyBytes(bytes int) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.TracingMaxBodyBytes = bytes
|
|
}
|
|
}
|
|
|
|
func WithGeneratedContentDir(generatedContentDir string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.GeneratedContentDir = generatedContentDir
|
|
}
|
|
}
|
|
|
|
func WithUploadDir(uploadDir string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.UploadDir = uploadDir
|
|
}
|
|
}
|
|
|
|
func WithDataPath(dataPath string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.DataPath = dataPath
|
|
}
|
|
}
|
|
|
|
// WithDisableStats turns off the billing recorder. CLI: --disable-stats.
|
|
func WithDisableStats(disable bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.DisableStats = disable
|
|
}
|
|
}
|
|
|
|
// WithMITMListen sets the address the cloudproxy MITM listener
|
|
// binds on. Empty = disabled. CLI: --mitm-listen.
|
|
func WithMITMListen(addr string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.MITMListen = addr
|
|
}
|
|
}
|
|
|
|
// WithMITMCADir sets the directory used to persist the MITM proxy
|
|
// CA cert + key. CLI: --mitm-ca-dir.
|
|
func WithMITMCADir(dir string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.MITMCADir = dir
|
|
}
|
|
}
|
|
|
|
// WithPIIDefaultDetectors sets the instance-wide default PII/secret detector
|
|
// model names applied to any PII-enabled model (chiefly cloud-proxy / MITM
|
|
// models) that names no pii.detectors of its own. CLI/env:
|
|
// LOCALAI_PII_DEFAULT_DETECTORS. Empty leaves the value to
|
|
// runtime_settings.json / the Middleware UI; a non-empty value takes
|
|
// precedence over the file (env > file).
|
|
func WithPIIDefaultDetectors(detectors []string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.PIIDefaultDetectors = detectors
|
|
}
|
|
}
|
|
|
|
func WithDynamicConfigDir(dynamicConfigsDir string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.DynamicConfigsDir = dynamicConfigsDir
|
|
}
|
|
}
|
|
|
|
func WithDynamicConfigDirPollInterval(interval time.Duration) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.DynamicConfigsDirPollInterval = interval
|
|
}
|
|
}
|
|
|
|
func WithApiKeys(apiKeys []string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.ApiKeys = apiKeys
|
|
}
|
|
}
|
|
|
|
func WithOpenResponsesStoreTTL(ttl time.Duration) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.OpenResponsesStoreTTL = ttl
|
|
}
|
|
}
|
|
|
|
func WithEnforcedPredownloadScans(enforced bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.EnforcePredownloadScans = enforced
|
|
}
|
|
}
|
|
|
|
func WithOpaqueErrors(opaque bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.OpaqueErrors = opaque
|
|
}
|
|
}
|
|
|
|
func WithLoadToMemory(models []string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.LoadToMemory = models
|
|
}
|
|
}
|
|
|
|
func WithSubtleKeyComparison(subtle bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.UseSubtleKeyComparison = subtle
|
|
}
|
|
}
|
|
|
|
func WithDisableApiKeyRequirementForHttpGet(required bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.DisableApiKeyRequirementForHttpGet = required
|
|
}
|
|
}
|
|
|
|
func WithAPIAddress(address string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.APIAddress = address
|
|
}
|
|
}
|
|
|
|
var DisableMetricsEndpoint AppOption = func(o *ApplicationConfig) {
|
|
o.DisableMetrics = true
|
|
}
|
|
|
|
func WithHttpGetExemptedEndpoints(endpoints []string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.HttpGetExemptedEndpoints = []*regexp.Regexp{}
|
|
for _, epr := range endpoints {
|
|
r, err := regexp.Compile(epr)
|
|
if err == nil && r != nil {
|
|
o.HttpGetExemptedEndpoints = append(o.HttpGetExemptedEndpoints, r)
|
|
} else {
|
|
xlog.Warn("Error while compiling HTTP Get Exemption regex, skipping this entry.", "error", err, "regex", epr)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Agent Pool options
|
|
|
|
var DisableCollections = func(o *ApplicationConfig) {
|
|
o.Collections.Enabled = false
|
|
}
|
|
|
|
func WithCollectionsAPIURL(url string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Collections.APIURL = url
|
|
}
|
|
}
|
|
|
|
func WithCollectionsAPIKey(key string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Collections.APIKey = key
|
|
}
|
|
}
|
|
|
|
func WithCollectionsDefaultModel(model string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Collections.DefaultModel = model
|
|
}
|
|
}
|
|
|
|
func WithCollectionsStateDir(dir string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Collections.StateDir = dir
|
|
}
|
|
}
|
|
|
|
func WithCollectionsVectorEngine(engine string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Collections.VectorEngine = engine
|
|
}
|
|
}
|
|
|
|
func WithCollectionsEmbeddingModel(model string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Collections.EmbeddingModel = model
|
|
}
|
|
}
|
|
|
|
func WithCollectionsDatabaseURL(url string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Collections.DatabaseURL = url
|
|
}
|
|
}
|
|
|
|
func WithCollectionsMaxChunkingSize(size int) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Collections.MaxChunkingSize = size
|
|
}
|
|
}
|
|
|
|
func WithCollectionsChunkOverlap(overlap int) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Collections.ChunkOverlap = overlap
|
|
}
|
|
}
|
|
|
|
func WithCollectionsCollectionDBPath(path string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Collections.CollectionDBPath = path
|
|
}
|
|
}
|
|
|
|
// Auth options
|
|
|
|
func WithAuthEnabled(enabled bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Auth.Enabled = enabled
|
|
}
|
|
}
|
|
|
|
func WithAuthDatabaseURL(url string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Auth.DatabaseURL = url
|
|
}
|
|
}
|
|
|
|
func WithAuthGitHubClientID(clientID string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Auth.GitHubClientID = clientID
|
|
}
|
|
}
|
|
|
|
func WithAuthGitHubClientSecret(clientSecret string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Auth.GitHubClientSecret = clientSecret
|
|
}
|
|
}
|
|
|
|
func WithExternalBaseURL(url string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.ExternalBaseURL = url
|
|
}
|
|
}
|
|
|
|
func WithAuthAdminEmail(email string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Auth.AdminEmail = email
|
|
}
|
|
}
|
|
|
|
func WithAuthRegistrationMode(mode string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Auth.RegistrationMode = mode
|
|
}
|
|
}
|
|
|
|
func WithAuthDisableLocalAuth(disable bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Auth.DisableLocalAuth = disable
|
|
}
|
|
}
|
|
|
|
func WithAuthOIDCIssuer(issuer string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Auth.OIDCIssuer = issuer
|
|
}
|
|
}
|
|
|
|
func WithAuthOIDCClientID(clientID string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Auth.OIDCClientID = clientID
|
|
}
|
|
}
|
|
|
|
func WithAuthOIDCClientSecret(clientSecret string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Auth.OIDCClientSecret = clientSecret
|
|
}
|
|
}
|
|
|
|
func WithAuthAPIKeyHMACSecret(secret string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Auth.APIKeyHMACSecret = secret
|
|
}
|
|
}
|
|
|
|
func WithAuthDefaultAPIKeyExpiry(expiry string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.Auth.DefaultAPIKeyExpiry = expiry
|
|
}
|
|
}
|
|
|
|
// WithLegacyNavigation brings the old sidebar back without a rebuild. The UI
|
|
// reads it from /api/features. It is a rollback switch and is removed one
|
|
// release after the add-on navigation ships.
|
|
func WithLegacyNavigation(on bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.LegacyNavigation = on
|
|
}
|
|
}
|
|
|
|
// DisableChats, DisableTriggers and DisableConnectors are the options of the
|
|
// operator switches LOCALAI_DISABLE_CHATS, LOCALAI_DISABLE_TRIGGERS and
|
|
// LOCALAI_DISABLE_CONNECTORS.
|
|
var (
|
|
DisableChats = func(o *ApplicationConfig) { o.DisableChats = true }
|
|
DisableTriggers = func(o *ApplicationConfig) { o.DisableTriggers = true }
|
|
DisableConnectors = func(o *ApplicationConfig) { o.DisableConnectors = true }
|
|
// DisableConnectorTalkback is the option of LOCALAI_DISABLE_CONNECTOR_TALKBACK.
|
|
DisableConnectorTalkback = func(o *ApplicationConfig) { o.DisableConnectorTalkback = true }
|
|
// SkipBindingCapsMigration is the option of LOCALAI_SKIP_BINDING_CAPS_MIGRATION.
|
|
SkipBindingCapsMigration = func(o *ApplicationConfig) { o.SkipBindingCapsMigration = true }
|
|
)
|
|
|
|
// WithAgentJobRetentionDays sets how long finished agent jobs are kept.
|
|
func WithAgentJobRetentionDays(days int) AppOption {
|
|
return func(o *ApplicationConfig) { o.AgentJobRetentionDays = days }
|
|
}
|
|
|
|
// WithMCPCIJobTimeout sets the longest an agent job may run.
|
|
func WithMCPCIJobTimeout(d time.Duration) AppOption {
|
|
return func(o *ApplicationConfig) { o.MCPCIJobTimeout = d }
|
|
}
|
|
|
|
// WithTriggerRunRetention sets how many finished runs are kept for each
|
|
// trigger, and for how many days.
|
|
func WithTriggerRunRetention(keep, days int) AppOption {
|
|
return func(o *ApplicationConfig) { o.TriggerRunKeep, o.TriggerRunRetentionDays = keep, days }
|
|
}
|
|
|
|
// Defaults of the agent jobs.
|
|
const (
|
|
DefaultAgentJobRetentionDays = 30
|
|
DefaultMCPCIJobTimeout = 10 * time.Minute
|
|
)
|
|
|
|
// WithChatsHostExec sets who gets the chats' built-in bash: off, ask or on.
|
|
func WithChatsHostExec(mode string) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.SetChatsHostExec(mode)
|
|
}
|
|
}
|
|
|
|
// WithDisableSharedPages turns off the pages of shared folders.
|
|
func WithDisableSharedPages(disabled bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.DisableSharedPages = disabled
|
|
}
|
|
}
|
|
|
|
// WithDisableLocalAIAssistant hard-disables the in-process admin MCP server.
|
|
// When set, the chat-handler branch for metadata.localai_assistant=true
|
|
// returns a "feature unavailable" error.
|
|
func WithDisableLocalAIAssistant(disabled bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.DisableLocalAIAssistant = disabled
|
|
}
|
|
}
|
|
|
|
// WithExposeNodeHeader enables the X-LocalAI-Node response header on
|
|
// inference endpoints. Default off; the node ID reveals internal cluster
|
|
// topology and is opt-in for that reason.
|
|
func WithExposeNodeHeader(enabled bool) AppOption {
|
|
return func(o *ApplicationConfig) {
|
|
o.ExposeNodeHeader = enabled
|
|
}
|
|
}
|
|
|
|
// ToConfigLoaderOptions returns a slice of ConfigLoader Option.
|
|
// Some options defined at the application level are going to be passed as defaults for
|
|
// all the configuration for the models.
|
|
// This includes for instance the context size or the number of threads.
|
|
// If a model doesn't set configs directly to the config model file
|
|
// it will use the defaults defined here.
|
|
func (o *ApplicationConfig) ToConfigLoaderOptions() []ConfigLoaderOption {
|
|
return []ConfigLoaderOption{
|
|
LoadOptionContextSize(o.ContextSize),
|
|
LoadOptionDebug(o.Debug),
|
|
LoadOptionF16(o.F16),
|
|
LoadOptionThreads(o.Threads),
|
|
ModelPath(o.SystemState.Model.ModelsPath),
|
|
LoadOptionGalleryFiles(o.Galleries...),
|
|
// Select later YAML/default phases; early phases use WithReloadDiagnostics
|
|
// at loader construction so callbacks retain their original execution point.
|
|
LoadOptionDiagnostics(o.DiagnosticsRecorder),
|
|
}
|
|
}
|
|
|
|
// ToRuntimeSettings converts ApplicationConfig to RuntimeSettings for API
|
|
// responses and JSON serialization. Field coverage comes entirely from the
|
|
// runtimeSettingsFields registry (runtime_settings_registry.go).
|
|
func (o *ApplicationConfig) ToRuntimeSettings() RuntimeSettings {
|
|
var s RuntimeSettings
|
|
for _, f := range runtimeSettingsFields {
|
|
f.snapshot(o, &s)
|
|
}
|
|
return s
|
|
}
|
|
|
|
// ApplyRuntimeSettings applies RuntimeSettings to ApplicationConfig (the
|
|
// live POST /api/settings path). Only non-nil fields are applied. Returns
|
|
// true when legacy service-reconfiguration fields were accepted (see each
|
|
// row's restartRequired flag in runtimeSettingsFields). The settings endpoint
|
|
// consumes this as a watchdog reconfiguration signal, not a server restart.
|
|
func (o *ApplicationConfig) ApplyRuntimeSettings(settings *RuntimeSettings) (requireRestart bool) {
|
|
if settings == nil {
|
|
return false
|
|
}
|
|
for _, f := range runtimeSettingsFields {
|
|
if f.snapshotOnly || !f.isSet(settings) {
|
|
continue
|
|
}
|
|
if f.apply(o, settings) && f.requiresRestart {
|
|
requireRestart = true
|
|
}
|
|
}
|
|
// The React Settings "Enable Watchdog" master toggle manages only the
|
|
// idle/busy checks - watchdog_enabled is vestigial in that UI. Whenever
|
|
// either idle/busy field is present in the body, derive the run-state
|
|
// from idle||busy so a cold enable starts the watchdog and a full
|
|
// disable stops it, instead of trusting the stale watchdog_enabled the
|
|
// UI never updates. An API client posting only watchdog_enabled
|
|
// (idle/busy absent) keeps its explicit value. (#9125)
|
|
if settings.WatchdogIdleEnabled != nil || settings.WatchdogBusyEnabled != nil {
|
|
o.WatchDog = o.WatchDogIdle || o.WatchDogBusy
|
|
}
|
|
// The memory reclaimer needs the watchdog run loop.
|
|
if settings.MemoryReclaimerEnabled != nil && *settings.MemoryReclaimerEnabled {
|
|
o.WatchDog = true
|
|
}
|
|
// VRAM budget live-apply: a cross-cutting process-global side effect
|
|
// (xsysinfo), kept out of the registry row like the watchdog/reclaimer
|
|
// invariants above - rows only own config members. An empty string
|
|
// clears the cap; a malformed value is rejected by the settings
|
|
// endpoint, but stay fail-open here too so a bad persisted value
|
|
// cannot wedge apply.
|
|
if settings.VRAMBudget != nil {
|
|
if b, err := vrambudget.Parse(o.VRAMBudget); err == nil {
|
|
xsysinfo.SetDefaultVRAMBudget(b)
|
|
}
|
|
}
|
|
if settings.ArtifactDownloadConcurrency != nil {
|
|
if configurable, ok := o.ModelArtifactMaterializer.(interface{ SetDownloadConcurrency(int) }); ok {
|
|
configurable.SetDownloadConcurrency(o.ArtifactDownloadConcurrency)
|
|
}
|
|
}
|
|
// Note: ApiKeys need env-merge handling (MergeAPIKeys) - done by the
|
|
// caller, because the env-provided keys live on the startup config.
|
|
return requireRestart
|
|
}
|
|
|
|
// func WithMetrics(meter *metrics.Metrics) AppOption {
|
|
// return func(o *StartupOptions) {
|
|
// o.Metrics = meter
|
|
// }
|
|
// }
|
|
|
|
// WithRouterDecisionLog retains routing decisions independently of billing stats.
|
|
func WithRouterDecisionLog(enabled bool) AppOption {
|
|
return func(o *ApplicationConfig) { o.RouterDecisionLog = enabled }
|
|
}
|
|
|
|
// WithDiagnosticsRecorder installs the optional startup-only timing recorder.
|
|
func WithDiagnosticsRecorder(r *diagnostics.Recorder) AppOption {
|
|
return func(o *ApplicationConfig) { o.DiagnosticsRecorder = r }
|
|
}
|
|
|
|
// DefaultChatsMaxIterations is the step limit of a chat turn when
|
|
// chats_max_iterations is not set. Multi-step work (read files, run commands,
|
|
// write a result) often needs a few dozen rounds of tool calls.
|
|
const DefaultChatsMaxIterations = 40
|
|
|
|
// MaxChatsMaxIterations is the highest step limit chats_max_iterations takes.
|
|
const MaxChatsMaxIterations = 500
|
|
|
|
// Bounds and defaults of the loop settings of the chats.
|
|
const (
|
|
MinChatsLoopInterval = 30 * time.Second
|
|
MaxChatsLoopInterval = 24 * time.Hour
|
|
DefaultChatsLoopMinInterval = time.Minute
|
|
DefaultChatsLoopMaxRuns = 100
|
|
MaxChatsLoopMaxRuns = 10000
|
|
DefaultChatsLoopMaxPerChat = 3
|
|
MaxChatsLoopMaxPerChat = 20
|
|
DefaultChatsLoopMaxPerUser = 10
|
|
MaxChatsLoopMaxPerUser = 200
|
|
DefaultChatsLoopDailyRuns = 200
|
|
MaxChatsLoopDailyRuns = 100000
|
|
)
|
|
|
|
// EffectiveChatsLoopMinInterval is chats_loop_min_interval as it applies now.
|
|
func (o *ApplicationConfig) EffectiveChatsLoopMinInterval() time.Duration {
|
|
o.chatsLimitsMu.RLock()
|
|
defer o.chatsLimitsMu.RUnlock()
|
|
if d := o.ChatsLoopMinInterval; d >= MinChatsLoopInterval && d <= MaxChatsLoopInterval {
|
|
return d
|
|
}
|
|
return DefaultChatsLoopMinInterval
|
|
}
|
|
|
|
// inRange is n when it is from 1 to hi, else def.
|
|
func inRange(n, hi, def int) int {
|
|
if n >= 1 && n <= hi {
|
|
return n
|
|
}
|
|
return def
|
|
}
|
|
|
|
// EffectiveChatsLoopMaxRuns is chats_loop_max_runs as it applies now.
|
|
func (o *ApplicationConfig) EffectiveChatsLoopMaxRuns() int {
|
|
o.chatsLimitsMu.RLock()
|
|
defer o.chatsLimitsMu.RUnlock()
|
|
return inRange(o.ChatsLoopMaxRuns, MaxChatsLoopMaxRuns, DefaultChatsLoopMaxRuns)
|
|
}
|
|
|
|
// EffectiveChatsLoopMaxPerChat is chats_loop_max_per_chat as it applies now.
|
|
func (o *ApplicationConfig) EffectiveChatsLoopMaxPerChat() int {
|
|
o.chatsLimitsMu.RLock()
|
|
defer o.chatsLimitsMu.RUnlock()
|
|
return inRange(o.ChatsLoopMaxPerChat, MaxChatsLoopMaxPerChat, DefaultChatsLoopMaxPerChat)
|
|
}
|
|
|
|
// EffectiveChatsLoopMaxPerUser is chats_loop_max_per_user as it applies now.
|
|
func (o *ApplicationConfig) EffectiveChatsLoopMaxPerUser() int {
|
|
o.chatsLimitsMu.RLock()
|
|
defer o.chatsLimitsMu.RUnlock()
|
|
return inRange(o.ChatsLoopMaxPerUser, MaxChatsLoopMaxPerUser, DefaultChatsLoopMaxPerUser)
|
|
}
|
|
|
|
// EffectiveChatsLoopDailyRuns is chats_loop_daily_runs as it applies now.
|
|
func (o *ApplicationConfig) EffectiveChatsLoopDailyRuns() int {
|
|
o.chatsLimitsMu.RLock()
|
|
defer o.chatsLimitsMu.RUnlock()
|
|
return inRange(o.ChatsLoopDailyRuns, MaxChatsLoopDailyRuns, DefaultChatsLoopDailyRuns)
|
|
}
|
|
|
|
// Bounds of chats_index_max_text_bytes.
|
|
const (
|
|
MinChatsIndexMaxTextBytes = 64 << 10
|
|
MaxChatsIndexMaxTextBytes = 16 << 20
|
|
DefaultChatsIndexMaxTextBytes = 2 << 20
|
|
)
|
|
|
|
// EffectiveChatsIndexMaxTextBytes is the text cap of one indexed file that
|
|
// applies: the setting, or the default when it is unset or out of range.
|
|
func (o *ApplicationConfig) EffectiveChatsIndexMaxTextBytes() int {
|
|
if n := o.ChatsIndexMaxTextBytes; n >= MinChatsIndexMaxTextBytes && n <= MaxChatsIndexMaxTextBytes {
|
|
return n
|
|
}
|
|
return DefaultChatsIndexMaxTextBytes
|
|
}
|
|
|
|
// DefaultChatsEgressAuditDays is how long the Sandbox network audit keeps a
|
|
// line while chats_egress_audit_days is unset.
|
|
const DefaultChatsEgressAuditDays = 30
|
|
|
|
// setChatsEgressAuditDays stores chats_egress_audit_days under the lock that
|
|
// the prune goroutine's reads take.
|
|
func (o *ApplicationConfig) setChatsEgressAuditDays(v int) {
|
|
o.chatsLimitsMu.Lock()
|
|
o.ChatsEgressAuditDays = v
|
|
o.chatsLimitsMu.Unlock()
|
|
}
|
|
|
|
// EffectiveChatsEgressAuditDays is chats_egress_audit_days as it applies now.
|
|
func (o *ApplicationConfig) EffectiveChatsEgressAuditDays() int {
|
|
o.chatsLimitsMu.RLock()
|
|
defer o.chatsLimitsMu.RUnlock()
|
|
if o.ChatsEgressAuditDays < 1 || o.ChatsEgressAuditDays > 365 {
|
|
return DefaultChatsEgressAuditDays
|
|
}
|
|
return o.ChatsEgressAuditDays
|
|
}
|
|
|
|
// Defaults and bounds of chats_goal_max_turns and chats_goal_max_steps.
|
|
const (
|
|
DefaultChatsGoalMaxTurns = 20
|
|
MaxChatsGoalMaxTurns = 200
|
|
DefaultChatsGoalMaxSteps = 400
|
|
MaxChatsGoalMaxSteps = 10000
|
|
)
|
|
|
|
// EffectiveChatsGoalMaxTurns is chats_goal_max_turns as it applies now.
|
|
func (o *ApplicationConfig) EffectiveChatsGoalMaxTurns() int {
|
|
if o.ChatsGoalMaxTurns < 1 || o.ChatsGoalMaxTurns > MaxChatsGoalMaxTurns {
|
|
return DefaultChatsGoalMaxTurns
|
|
}
|
|
return o.ChatsGoalMaxTurns
|
|
}
|
|
|
|
// EffectiveChatsGoalMaxSteps is chats_goal_max_steps as it applies now.
|
|
func (o *ApplicationConfig) EffectiveChatsGoalMaxSteps() int {
|
|
if o.ChatsGoalMaxSteps < 1 || o.ChatsGoalMaxSteps > MaxChatsGoalMaxSteps {
|
|
return DefaultChatsGoalMaxSteps
|
|
}
|
|
return o.ChatsGoalMaxSteps
|
|
}
|
|
|
|
// EffectiveChatsMaxIterations is chats_max_iterations as it applies now.
|
|
func (o *ApplicationConfig) EffectiveChatsMaxIterations() int {
|
|
if o.ChatsMaxIterations < 1 || o.ChatsMaxIterations > MaxChatsMaxIterations {
|
|
return DefaultChatsMaxIterations
|
|
}
|
|
return o.ChatsMaxIterations
|
|
}
|
|
|
|
func WithP2PEnableLimits(enabled bool) AppOption {
|
|
return func(o *ApplicationConfig) { o.P2PEnableLimits = enabled }
|
|
}
|