fix(api): report an alias's target in /v1/models/capabilities (#12183)

An alias config is a pure redirect with no backend of its own, so the
capabilities listing described it from its stub: no capabilities, no
modalities, and the default 4096 context_size. Clients that size their
context budget from this endpoint (nib, for one) then compacted every
turn against a model that really serves 100k.

Resolve the alias and report the target's capabilities, modalities and
context_size under the alias's id. A dangling or chained alias now
reports no enrichment instead of defaults no model runs with.

Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-09-21 10:51:17 +02:00
1 parent 08158a5c4d
commit 36d800ebd1
2 files changed
+60 -1

No files matched your search

@@ -35,7 +35,7 @@ func ListModelCapabilitiesEndpoint(bcl *config.ModelConfigLoader, ml *model.Mode
dataModels := []schema.ModelCapabilities{}
for _, m := range modelNames {
entry := schema.ModelCapabilities{ID: m, Object: "model"}
if cfg, ok := bcl.GetModelConfig(m); ok {
if cfg, ok := modelConfigFor(bcl, m); ok {
entry.Capabilities = cfg.Capabilities()
entry.ThreeDOperations = cfg.ThreeDOperations()
entry.InputModalities = cfg.InputModalities()
@@ -53,3 +53,22 @@ func ListModelCapabilitiesEndpoint(bcl *config.ModelConfigLoader, ml *model.Mode
})
}
}
// modelConfigFor returns the config that describes what a listed model can
// do. An alias is a pure redirect whose own config carries no backend, so its
// capabilities, modalities and context_size would all be empty defaults;
// report the target's instead, since that is the model a request for the
// alias reaches. A dangling or chained alias returns false: the endpoint then
// reports the entry without enrichment rather than advertise defaults no
// model runs with.
func modelConfigFor(bcl *config.ModelConfigLoader, name string) (config.ModelConfig, bool) {
cfg, ok := bcl.GetModelConfig(name)
if !ok {
return config.ModelConfig{}, false
}
resolved, _, err := bcl.ResolveAlias(&cfg)
if err != nil {
return config.ModelConfig{}, false
}
return *resolved, true
}
@@ -142,4 +142,44 @@ parameters:
Expect(entry).NotTo(BeNil())
Expect(entry.ContextSize).To(Equal(backend.DefaultContextSize))
})
It("reports an alias with its target's capabilities and context_size", func() {
writeConfig("real-llm", `
name: real-llm
backend: llama-cpp
context_size: 100000
known_usecases:
- FLAG_CHAT
- FLAG_VISION
template:
chat: "{{ .Input }}"
parameters:
model: model.gguf
`)
writeConfig("friendly", `
name: friendly
alias: real-llm
`)
resp := call()
target := entryFor(resp, "real-llm")
Expect(target).NotTo(BeNil())
entry := entryFor(resp, "friendly")
Expect(entry).NotTo(BeNil())
Expect(entry.ID).To(Equal("friendly"))
Expect(entry.ContextSize).To(Equal(100000))
Expect(entry.Capabilities).To(Equal(target.Capabilities))
Expect(entry.InputModalities).To(Equal(target.InputModalities))
Expect(entry.OutputModalities).To(Equal(target.OutputModalities))
})
It("reports no context_size for an alias whose target does not exist", func() {
writeConfig("dangling", `
name: dangling
alias: missing-model
`)
entry := entryFor(call(), "dangling")
Expect(entry).NotTo(BeNil())
Expect(entry.ContextSize).To(BeZero())
Expect(entry.Capabilities).To(BeEmpty())
})
})