From 411d01a7042cc7077ea222e69d3a6bcf2e1c83c7 Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Sat, 20 Jun 2026 09:30:44 +0000 Subject: [PATCH] feat(config): add model alias field and self-validation Add ModelConfig.Alias (yaml: alias), IsAlias(), and an alias short-circuit at the top of Validate() that rejects self-reference and forbids setting backend/parameters.model on a pure-redirect alias. Assisted-by: Claude:claude-opus-4-8 [Claude Code] Signed-off-by: Ettore Di Giacinto --- core/config/model_config.go | 26 ++++++++++++++++++++++++++ core/config/model_config_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/core/config/model_config.go b/core/config/model_config.go index dfe151a64..661624470 100644 --- a/core/config/model_config.go +++ b/core/config/model_config.go @@ -37,6 +37,12 @@ type ModelConfig struct { schema.PredictionOptions `yaml:"parameters,omitempty" json:"parameters,omitempty"` Name string `yaml:"name,omitempty" json:"name,omitempty"` + // Alias, when set, makes this config a pure redirect: every request for + // Name is served by the model named here. All other fields are ignored. + // The target must be an existing, non-alias model (enforced at load and + // at create/swap time). See docs/content for Model Aliases. + Alias string `yaml:"alias,omitempty" json:"alias,omitempty"` + F16 *bool `yaml:"f16,omitempty" json:"f16,omitempty"` Threads *int `yaml:"threads,omitempty" json:"threads,omitempty"` Debug *bool `yaml:"debug,omitempty" json:"debug,omitempty"` @@ -391,6 +397,10 @@ func (c *ModelConfig) HasRouter() bool { return len(c.Router.Candidates) > 0 } +// IsAlias reports whether this config is a pure redirect to another model. +// Value receiver so it is callable on non-addressable config values too. +func (c ModelConfig) IsAlias() bool { return c.Alias != "" } + // @Description PII filtering configuration. PII redaction is per-model so // that local models don't pay the latency or behaviour change of regex // scanning, while cloud-bound traffic (cloud-proxy backend) can default to @@ -1243,6 +1253,22 @@ func (cfg *ModelConfig) SetDefaults(opts ...ConfigLoaderOption) { } func (c *ModelConfig) Validate() (bool, error) { + // An alias is a pure redirect: validate only its own shape here. Target + // existence and the no-chain rule need the full config set, so the loader + // (load-time) and the create/swap endpoints enforce those. + if c.IsAlias() { + if c.Name == "" { + return false, fmt.Errorf("alias config requires a name") + } + if c.Alias == c.Name { + return false, fmt.Errorf("alias %q cannot point to itself", c.Name) + } + if c.Backend != "" || c.Model != "" { + return false, fmt.Errorf("alias config %q must not set backend or parameters.model: an alias is a pure redirect", c.Name) + } + return true, nil + } + downloadedFileNames := []string{} for _, f := range c.DownloadFiles { downloadedFileNames = append(downloadedFileNames, f.Filename) diff --git a/core/config/model_config_test.go b/core/config/model_config_test.go index 7f256354d..2f2f3fd82 100644 --- a/core/config/model_config_test.go +++ b/core/config/model_config_test.go @@ -787,3 +787,32 @@ var _ = Describe("pattern detector config", func() { Expect(err).To(MatchError(ContainSubstring("pattern \"EMAILish\""))) }) }) + +var _ = Describe("ModelConfig alias", func() { + It("reports IsAlias when alias is set", func() { + c := ModelConfig{Name: "gpt-4", Alias: "my-llama-3"} + Expect(c.IsAlias()).To(BeTrue()) + Expect(ModelConfig{Name: "real"}.IsAlias()).To(BeFalse()) + }) + + It("validates a minimal alias config", func() { + c := ModelConfig{Name: "gpt-4", Alias: "my-llama-3"} + ok, err := c.Validate() + Expect(err).ToNot(HaveOccurred()) + Expect(ok).To(BeTrue()) + }) + + It("rejects an alias pointing to itself", func() { + c := ModelConfig{Name: "loop", Alias: "loop"} + ok, err := c.Validate() + Expect(ok).To(BeFalse()) + Expect(err).To(MatchError(ContainSubstring("itself"))) + }) + + It("rejects an alias that also sets a backend", func() { + c := ModelConfig{Name: "gpt-4", Alias: "my-llama-3", Backend: "llama-cpp"} + ok, err := c.Validate() + Expect(ok).To(BeFalse()) + Expect(err).To(MatchError(ContainSubstring("pure redirect"))) + }) +})