mirror of
https://github.com/mudler/LocalAI.git
synced 2026-10-03 03:24:34 -04:00
feat(systemone): refuse models that do not declare the usecase
A chat-only model now gets a 400 naming known_usecases: [systemone] instead of a backend error. Configs declaring no usecases and token_classify models stay allowed so existing laya and GLiNER setups keep working. Assisted-by: Claude Code:claude-sonnet-5-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
1 parent
84c83a70dd
commit
e03cf8dac8
2 files changed
+72
No files matched your search
@@ -371,6 +371,35 @@ func systemOneError(c echo.Context, status int, msg string) error {
|
||||
})
|
||||
}
|
||||
|
||||
// systemOneModelAllowed keeps chat and embedding models out of the decision
|
||||
// API with an actionable error instead of a backend failure. A config that
|
||||
// declares no usecases predates the flag and stays allowed, and a
|
||||
// token_classify model is allowed because the NER path serves it.
|
||||
func systemOneModelAllowed(cfg config.ModelConfig) error {
|
||||
if cfg.KnownUsecases == nil {
|
||||
return nil
|
||||
}
|
||||
if *cfg.KnownUsecases&(config.FLAG_SYSTEMONE|config.FLAG_TOKEN_CLASSIFY) != 0 {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("model %q does not declare the systemone usecase (known_usecases: [systemone])", cfg.Name)
|
||||
}
|
||||
|
||||
// checkSystemOneModel applies systemOneModelAllowed to a model looked up by
|
||||
// name. An unknown model passes here so the existing not-found handling
|
||||
// downstream keeps its status code.
|
||||
func checkSystemOneModel(app *application.Application, modelName string) error {
|
||||
cl := app.ModelConfigLoader()
|
||||
if cl == nil {
|
||||
return nil
|
||||
}
|
||||
cfg, ok := cl.GetModelConfig(modelName)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return systemOneModelAllowed(cfg)
|
||||
}
|
||||
|
||||
// backendSupportsScore reports whether the named backend implements the
|
||||
// Score gRPC RPC. vllm-cpp does (kev/laya decision pipeline and cua-s1-forms
|
||||
// scoring via the unified vllm_decide C ABI); other backends fall through to
|
||||
@@ -408,6 +437,9 @@ func SystemOneEndpoint(app *application.Application) echo.HandlerFunc {
|
||||
if req.Model == "" {
|
||||
return systemOneError(c, http.StatusBadRequest, "model is required")
|
||||
}
|
||||
if err := checkSystemOneModel(app, req.Model); err != nil {
|
||||
return systemOneError(c, http.StatusBadRequest, err.Error())
|
||||
}
|
||||
// vllm-cpp models (kev/laya) implement the decision pipeline natively
|
||||
// via the vllm_decide C ABI. Forward the raw request JSON through the
|
||||
// Score RPC and return the backend's response as-is.
|
||||
@@ -474,6 +506,9 @@ func SystemOnePermuteEndpoint(app *application.Application) echo.HandlerFunc {
|
||||
if req.Request.Model == "" {
|
||||
return systemOneError(c, http.StatusBadRequest, "model is required")
|
||||
}
|
||||
if err := checkSystemOneModel(app, req.Request.Model); err != nil {
|
||||
return systemOneError(c, http.StatusBadRequest, err.Error())
|
||||
}
|
||||
if req.Question == "" {
|
||||
return systemOneError(c, http.StatusBadRequest, "question is required")
|
||||
}
|
||||
@@ -610,6 +645,9 @@ func SystemOneSeparateEndpoint(app *application.Application) echo.HandlerFunc {
|
||||
if req.Model == "" {
|
||||
return systemOneError(c, http.StatusBadRequest, "model is required")
|
||||
}
|
||||
if err := checkSystemOneModel(app, req.Model); err != nil {
|
||||
return systemOneError(c, http.StatusBadRequest, err.Error())
|
||||
}
|
||||
parsed, err := parseSystemOneRequest(&req)
|
||||
if err != nil {
|
||||
return systemOneError(c, http.StatusBadRequest, err.Error())
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package localai
|
||||
|
||||
import (
|
||||
"github.com/mudler/LocalAI/core/config"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("systemOneModelAllowed", func() {
|
||||
mk := func(usecases ...string) config.ModelConfig {
|
||||
return config.ModelConfig{
|
||||
Name: "m",
|
||||
Backend: "vllm-cpp",
|
||||
KnownUsecases: config.GetUsecasesFromYAML(usecases),
|
||||
}
|
||||
}
|
||||
|
||||
It("accepts a declared systemone model", func() {
|
||||
Expect(systemOneModelAllowed(mk("systemone"))).To(Succeed())
|
||||
})
|
||||
|
||||
It("accepts a token_classify model, which the NER path serves", func() {
|
||||
Expect(systemOneModelAllowed(mk("token_classify"))).To(Succeed())
|
||||
})
|
||||
|
||||
It("keeps configs that declare no usecases working", func() {
|
||||
Expect(systemOneModelAllowed(config.ModelConfig{Name: "laya", Backend: "vllm-cpp"})).To(Succeed())
|
||||
})
|
||||
|
||||
It("refuses a chat-only model with an actionable message", func() {
|
||||
Expect(systemOneModelAllowed(mk("chat"))).To(MatchError(ContainSubstring("known_usecases: [systemone]")))
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user