From 8db6c15fe0b218f25ced16c391f777d50c3381bc Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Sun, 27 Sep 2026 07:33:27 +0000 Subject: [PATCH] refactor: name the proxy backends with constants Review asked for constants instead of the "cloud-proxy" and "localai-proxy" literals. Add CloudProxyBackend and LocalAIProxyBackend next to the other backend-name constants in pkg/model (WhisperBackend, TransformersBackend, ...), which core/config already imports, and use them in every production check: the proxy options builder, IsRemoteProxy, IsCloudProxyBackendPassthrough, the PII defaults, the localai-proxy backend hook and loader warning, and the PII middleware metadata in the routes and the in-process MCP client. The proxy options builder now calls IsRemoteProxy() instead of repeating the two-backend check, so the set of proxy backends is defined once. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Ettore Di Giacinto --- core/backend/options.go | 4 ++-- core/config/hooks_localai_proxy.go | 4 +++- core/config/model_config.go | 7 ++++--- core/config/model_config_failover.go | 4 +++- core/config/model_config_loader.go | 3 ++- core/http/routes/middleware.go | 5 +++-- pkg/mcp/localaitools/inproc/client.go | 4 ++-- pkg/model/initializers.go | 6 ++++++ 8 files changed, 25 insertions(+), 12 deletions(-) diff --git a/core/backend/options.go b/core/backend/options.go index 0f345e154..b2b652ac8 100644 --- a/core/backend/options.go +++ b/core/backend/options.go @@ -542,7 +542,7 @@ func grpcModelOpts(c config.ModelConfig, modelPath string) *pb.ModelOptions { Tokenizer: c.Tokenizer, } - if c.Backend == "cloud-proxy" || c.Backend == "localai-proxy" { + if c.IsRemoteProxy() { opts.Proxy = &pb.ProxyOptions{ UpstreamUrl: c.Proxy.UpstreamURL, Mode: c.Proxy.Mode, @@ -558,7 +558,7 @@ func grpcModelOpts(c config.ModelConfig, modelPath string) *pb.ModelOptions { // failover.UpstreamModel uses. Not for cloud-proxy: its translate mode // falls back to parameters.model and passthrough keeps the client's // model when upstream_model is empty. - if c.Backend == "localai-proxy" && opts.Proxy.UpstreamModel == "" { + if c.Backend == model.LocalAIProxyBackend && opts.Proxy.UpstreamModel == "" { opts.Proxy.UpstreamModel = c.Name } } diff --git a/core/config/hooks_localai_proxy.go b/core/config/hooks_localai_proxy.go index b1dd3bb36..5104fa6bc 100644 --- a/core/config/hooks_localai_proxy.go +++ b/core/config/hooks_localai_proxy.go @@ -1,7 +1,9 @@ package config +import "github.com/mudler/LocalAI/pkg/model" + func init() { - RegisterBackendHook("localai-proxy", localAIProxyDefaults) + RegisterBackendHook(model.LocalAIProxyBackend, localAIProxyDefaults) } // localAIProxyDefaults makes chat requests reach the upstream as structured diff --git a/core/config/model_config.go b/core/config/model_config.go index 7b67acb2a..14510d0eb 100644 --- a/core/config/model_config.go +++ b/core/config/model_config.go @@ -17,6 +17,7 @@ import ( "github.com/mudler/LocalAI/core/services/routing/piipattern" "github.com/mudler/LocalAI/pkg/downloader" "github.com/mudler/LocalAI/pkg/functions" + "github.com/mudler/LocalAI/pkg/model" "github.com/mudler/LocalAI/pkg/modelartifacts" "github.com/mudler/LocalAI/pkg/reasoning" "github.com/mudler/cogito" @@ -332,7 +333,7 @@ func (p ProxyConfig) ResolveAPIKey(envLookup func(string) string) (string, error // passthrough (SetDefaults normalises it, but Validate accepts empty // too — handlers should not rely on a particular call order). func (c *ModelConfig) IsCloudProxyBackendPassthrough() bool { - if c.Backend != "cloud-proxy" { + if c.Backend != model.CloudProxyBackend { return false } return c.Proxy.Mode == "" || c.Proxy.Mode == ProxyModePassthrough @@ -670,7 +671,7 @@ func (c *ModelConfig) PIIIsEnabled() bool { if c.PII.Enabled != nil { return *c.PII.Enabled } - return c.Backend == "cloud-proxy" + return c.Backend == model.CloudProxyBackend } // PIIDetectors returns the names of the token-classification models that @@ -701,7 +702,7 @@ var piiCoverableUsecases = []ModelConfigUsecase{FLAG_CHAT, FLAG_COMPLETION, FLAG // false naturally: HasUsecases short-circuits to false for any usecase a // declared score/token_classify model did not itself declare. func (c *ModelConfig) PIIFilterApplies() bool { - if c.Backend == "cloud-proxy" { + if c.Backend == model.CloudProxyBackend { return true } return slices.ContainsFunc(piiCoverableUsecases, c.HasUsecases) diff --git a/core/config/model_config_failover.go b/core/config/model_config_failover.go index f13ac2bdd..b4829d83e 100644 --- a/core/config/model_config_failover.go +++ b/core/config/model_config_failover.go @@ -3,6 +3,8 @@ package config import ( "fmt" "time" + + "github.com/mudler/LocalAI/pkg/model" ) // FailoverConfig turns a model config into a failover chain: requests for the @@ -54,7 +56,7 @@ func (c ModelConfig) IsFailover() bool { return c.Failover != nil } // `warm` has no effect on it. func (c ModelConfig) IsRemoteProxy() bool { switch c.Backend { - case "cloud-proxy", "localai-proxy": + case model.CloudProxyBackend, model.LocalAIProxyBackend: return true } return false diff --git a/core/config/model_config_loader.go b/core/config/model_config_loader.go index 5e979d774..7c1157ace 100644 --- a/core/config/model_config_loader.go +++ b/core/config/model_config_loader.go @@ -17,6 +17,7 @@ import ( "github.com/charmbracelet/glamour" "github.com/mudler/LocalAI/core/schema" "github.com/mudler/LocalAI/pkg/downloader" + "github.com/mudler/LocalAI/pkg/model" "github.com/mudler/LocalAI/pkg/modelartifacts" "github.com/mudler/LocalAI/pkg/safefile" "github.com/mudler/LocalAI/pkg/utils" @@ -963,7 +964,7 @@ func (bcl *ModelConfigLoader) loadModelConfigsFromPath(path string, strict bool, // when a config carries settings that only make sense there, or lacks // the usecases failover's own usecase-sharing check depends on. for name, cfg := range bcl.configs { - if cfg.Backend != "localai-proxy" { + if cfg.Backend != model.LocalAIProxyBackend { continue } if cfg.Proxy.Mode == ProxyModeTranslate || cfg.Proxy.Provider != "" { diff --git a/core/http/routes/middleware.go b/core/http/routes/middleware.go index 0195f445e..de43ecf43 100644 --- a/core/http/routes/middleware.go +++ b/core/http/routes/middleware.go @@ -12,6 +12,7 @@ import ( "github.com/mudler/LocalAI/core/http/endpoints/localai" "github.com/mudler/LocalAI/core/http/middleware" "github.com/mudler/LocalAI/core/services/routing/router" + "github.com/mudler/LocalAI/pkg/model" ) // RegisterMiddlewareRoutes wires the routing-module admin surface that @@ -348,7 +349,7 @@ func buildPIIStatus(app *application.Application) map[string]any { // Why is this on? backend default (cloud-proxy) vs an explicit YAML // toggle. Helps admins understand the resolved state without // reading source. - "default_for_backend": !explicit && cfg.Backend == "cloud-proxy", + "default_for_backend": !explicit && cfg.Backend == model.CloudProxyBackend, // The detectors came from the global default, not this model's YAML. "detectors_from_default": enabled && len(ownDetectors) == 0 && len(detectors) > 0, } @@ -403,7 +404,7 @@ func buildPIIStatus(app *application.Application) map[string]any { return map[string]any{ "enabled_globally": true, - "default_enabled_for_backends": []string{"cloud-proxy"}, + "default_enabled_for_backends": []string{model.CloudProxyBackend}, "models": models, "detector_models": detectorModels, "recent_event_count": recentCount, diff --git a/pkg/mcp/localaitools/inproc/client.go b/pkg/mcp/localaitools/inproc/client.go index aa3be911c..ff80b6cac 100644 --- a/pkg/mcp/localaitools/inproc/client.go +++ b/pkg/mcp/localaitools/inproc/client.go @@ -1008,7 +1008,7 @@ func (c *Client) GetMiddlewareStatus(ctx context.Context) (*localaitools.Middlew EnabledGlobally: c.PIIEvents != nil, Models: []localaitools.MiddlewarePIIModel{}, } - piiSection.DefaultEnabledForBackends = []string{"cloud-proxy"} + piiSection.DefaultEnabledForBackends = []string{model.CloudProxyBackend} if c.ConfigLoader != nil { for _, cfg := range c.ConfigLoader.GetAllModelsConfigs() { cfg := cfg @@ -1017,7 +1017,7 @@ func (c *Client) GetMiddlewareStatus(ctx context.Context) (*localaitools.Middlew Backend: cfg.Backend, Enabled: cfg.PIIIsEnabled(), Explicit: cfg.PII.Enabled != nil, - DefaultForBackend: cfg.Backend == "cloud-proxy", + DefaultForBackend: cfg.Backend == model.CloudProxyBackend, Detectors: cfg.PIIDetectors(), }) } diff --git a/pkg/model/initializers.go b/pkg/model/initializers.go index cbfb7840c..ce046e882 100644 --- a/pkg/model/initializers.go +++ b/pkg/model/initializers.go @@ -51,6 +51,12 @@ const ( TransformersBackend = "transformers" LocalStoreBackend = "local-store" ValkeyStoreBackend = "valkey-store" + + // Proxy backends serve a model by forwarding to another server instead + // of loading weights. Core special-cases both (credentials, failover + // kind, PII defaults), so every check goes through these names. + CloudProxyBackend = "cloud-proxy" + LocalAIProxyBackend = "localai-proxy" ) // starts the grpcModelProcess for the backend, and returns a grpc client