From 70ce62901ff0d44e7100d692ba2cb443f655d408 Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Wed, 30 Sep 2026 14:14:09 +0000 Subject: [PATCH] refactor: name the capability decisions instead of systemone The usecase describes what a model can do, and the category is the Decisions API. SystemOne stays as the wire contract: the /v1/systemone routes, the Score RPC question_type and the swagger tag are unchanged. The usecase, flag, auth feature, UI label, gallery tags and docs page are now decisions. Assisted-by: Claude Code:claude-sonnet-5-5 Signed-off-by: Ettore Di Giacinto --- core/config/backend_capabilities.go | 12 ++++---- core/config/gguf.go | 4 +-- core/config/model_config.go | 20 ++++++------- core/config/model_config_test.go | 8 ++--- core/gallery/vllm_cpp_tags_test.go | 2 +- core/http/auth/features.go | 10 +++---- ...one_test.go => features_decisions_test.go} | 8 ++--- core/http/auth/permissions.go | 4 +-- .../endpoints/localai/api_instructions.go | 4 +-- .../localai/api_instructions_test.go | 8 ++--- core/http/endpoints/localai/systemone.go | 10 +++---- .../endpoints/localai/systemone_gate_test.go | 14 ++++----- .../react-ui/e2e/models-lifecycle.spec.js | 6 ++-- .../react-ui/public/locales/de/models.json | 2 +- .../react-ui/public/locales/en/models.json | 2 +- .../react-ui/public/locales/es/models.json | 2 +- .../react-ui/public/locales/id/models.json | 2 +- .../react-ui/public/locales/it/models.json | 2 +- .../react-ui/public/locales/ko/models.json | 2 +- .../react-ui/public/locales/pt-BR/models.json | 2 +- .../react-ui/public/locales/zh-CN/models.json | 2 +- .../react-ui/src/pages/InstalledModels.jsx | 4 +-- core/http/react-ui/src/utils/capabilities.js | 2 +- docs/content/advanced/model-configuration.md | 4 +-- .../features/{systemone.md => decisions.md} | 30 ++++++++++--------- docs/content/features/vllm-cpp.md | 6 ++-- gallery/index.yaml | 8 ++--- 27 files changed, 91 insertions(+), 89 deletions(-) rename core/http/auth/{features_systemone_test.go => features_decisions_test.go} (70%) rename docs/content/features/{systemone.md => decisions.md} (78%) diff --git a/core/config/backend_capabilities.go b/core/config/backend_capabilities.go index b4c40248c..650bc3f3c 100644 --- a/core/config/backend_capabilities.go +++ b/core/config/backend_capabilities.go @@ -35,7 +35,7 @@ const ( UsecaseSpeakerRecognition = "speaker_recognition" UsecaseTokenClassify = "token_classify" UsecaseScore = "score" - UsecaseSystemOne = "systemone" + UsecaseDecisions = "decisions" ) // GRPCMethod identifies a Backend service RPC from backend.proto. @@ -217,10 +217,10 @@ var UsecaseInfoMap = map[string]UsecaseInfo{ GRPCMethod: MethodScore, Description: "Joint log-probability scoring of candidate continuations via the Score RPC. Declared explicitly via known_usecases and usable alongside generation usecases.", }, - UsecaseSystemOne: { - Flag: FLAG_SYSTEMONE, + UsecaseDecisions: { + Flag: FLAG_DECISIONS, GRPCMethod: MethodScore, - Description: "SystemOne decision API (POST /v1/systemone): typed choice, noul and score questions over a state text, answered by a non-generative decision model through the Score RPC (question_type systemone). Declared explicitly via known_usecases.", + Description: "Decision models (served by POST /v1/systemone): typed choice, noul and score questions over a state text, answered by a non-generative decision model through the Score RPC (question_type systemone). Declared explicitly via known_usecases.", }, } @@ -355,10 +355,10 @@ var BackendCapabilities = map[string]BackendCapability{ // model returns an error rather than silent garbage. "vllm-cpp": { GRPCMethods: []GRPCMethod{MethodPredict, MethodPredictStream, MethodGenerateVideo, MethodTokenClassify, MethodScore}, - PossibleUsecases: []string{UsecaseChat, UsecaseCompletion, UsecaseVision, UsecaseVideo, UsecaseTokenClassify, UsecaseScore, UsecaseSystemOne}, + PossibleUsecases: []string{UsecaseChat, UsecaseCompletion, UsecaseVision, UsecaseVideo, UsecaseTokenClassify, UsecaseScore, UsecaseDecisions}, DefaultUsecases: []string{UsecaseChat}, AcceptsImages: true, - Description: "vllm.cpp — the LocalAI team's C++20 port of vLLM; text generation, MiniMax-H3 video+audio generation, GLiNER2.5 zero-shot NER, cua-s1-forms scoring, and SystemOne decision models (kev, laya, CLM, GLiNER2.5-Decide, xor, nimble)", + Description: "vllm.cpp — the LocalAI team's C++20 port of vLLM; text generation, MiniMax-H3 video+audio generation, GLiNER2.5 zero-shot NER, cua-s1-forms scoring, and decision models (kev, laya, CLM, GLiNER2.5-Decide, xor, nimble)", }, "vllm-omni": { GRPCMethods: []GRPCMethod{MethodPredict, MethodPredictStream, MethodGenerateImage, MethodGenerateVideo, MethodTTS}, diff --git a/core/config/gguf.go b/core/config/gguf.go index f9b8c748f..fad00a6c7 100644 --- a/core/config/gguf.go +++ b/core/config/gguf.go @@ -16,14 +16,14 @@ import ( // reservedNonChatModel reports whether the operator reserved this model for an // internal primitive — the router score classifier or the PII NER -// token_classify tier, or a SystemOne decision head. Such a model has no chat template and must not be +// token_classify tier, or a decision head. Such a model has no chat template and must not be // given the generative-chat defaults the GGUF importer otherwise applies // (FLAG_CHAT, jinja templating): surfacing it in chat pickers defeats the // reservation. Operators who do want a combined model declare both usecases // explicitly — the combination is valid. func reservedNonChatModel(cfg *ModelConfig) bool { return cfg.KnownUsecases != nil && - (*cfg.KnownUsecases&(FLAG_SCORE|FLAG_TOKEN_CLASSIFY|FLAG_SYSTEMONE)) != 0 + (*cfg.KnownUsecases&(FLAG_SCORE|FLAG_TOKEN_CLASSIFY|FLAG_DECISIONS)) != 0 } // genAudioEncoderKey is the mmproj metadata flag llama.cpp's mtmd writes for a diff --git a/core/config/model_config.go b/core/config/model_config.go index a78e1db6c..bc084aa87 100644 --- a/core/config/model_config.go +++ b/core/config/model_config.go @@ -2056,12 +2056,12 @@ const ( FLAG_3D ModelConfigUsecase = 0b100000000000000000000000 FLAG_3D_ANIMATION ModelConfigUsecase = 1 << 24 - // Marks a model as wired for the SystemOne decision API (POST - // /v1/systemone: typed choice / noul / score questions over a state). + // Marks a model as a decision model: it answers typed choice / noul / + // score questions over a state (served by POST /v1/systemone). // Explicit only, like FLAG_SCORE: a decision model never generates // text, so guessing chat or embeddings for it would surface it in // pickers it cannot serve. - FLAG_SYSTEMONE ModelConfigUsecase = 1 << 25 + FLAG_DECISIONS ModelConfigUsecase = 1 << 25 // Common Subsets FLAG_LLM ModelConfigUsecase = FLAG_CHAT | FLAG_COMPLETION | FLAG_EDIT @@ -2125,7 +2125,7 @@ func GetAllModelConfigUsecases() map[string]ModelConfigUsecase { "FLAG_TOKEN_CLASSIFY": FLAG_TOKEN_CLASSIFY, "FLAG_3D": FLAG_3D, "FLAG_3D_ANIMATION": FLAG_3D_ANIMATION, - "FLAG_SYSTEMONE": FLAG_SYSTEMONE, + "FLAG_DECISIONS": FLAG_DECISIONS, } } @@ -2154,9 +2154,9 @@ func GetUsecasesFromYAML(input []string) *ModelConfigUsecase { // // Declared known_usecases are normally additive — the guessing heuristic // still adds whatever it can infer from backend/templates. The exceptions -// are FLAG_SCORE, FLAG_TOKEN_CLASSIFY and FLAG_SYSTEMONE: when the operator +// are FLAG_SCORE, FLAG_TOKEN_CLASSIFY and FLAG_DECISIONS: when the operator // declared any of them, they reserved the model for a direct-decode primitive -// (the router classifier, the PII NER tier, or a SystemOne decision head). Letting GuessUsecases +// (the router classifier, the PII NER tier, or a decision head). Letting GuessUsecases // paint chat/completion/embeddings on top would surface it in pickers it // was deliberately kept out of. So a declared score or token_classify // list is authoritative; declare the generation usecases explicitly @@ -2166,7 +2166,7 @@ func (c *ModelConfig) HasUsecases(u ModelConfigUsecase) bool { if (u & *c.KnownUsecases) == u { return true } - if (*c.KnownUsecases & (FLAG_SCORE | FLAG_TOKEN_CLASSIFY | FLAG_SYSTEMONE)) != 0 { + if (*c.KnownUsecases & (FLAG_SCORE | FLAG_TOKEN_CLASSIFY | FLAG_DECISIONS)) != 0 { return false } } @@ -2389,10 +2389,10 @@ func (c *ModelConfig) GuessUsecases(u ModelConfigUsecase) bool { return false } - if (u & FLAG_SYSTEMONE) == FLAG_SYSTEMONE { - // No heuristic: SystemOne intent is a deliberate operator choice + if (u & FLAG_DECISIONS) == FLAG_DECISIONS { + // No heuristic: decisions intent is a deliberate operator choice // (the model is a non-generative decision head), so - // HasUsecases(FLAG_SYSTEMONE) is true only when KnownUsecases + // HasUsecases(FLAG_DECISIONS) is true only when KnownUsecases // declares it explicitly. return false } diff --git a/core/config/model_config_test.go b/core/config/model_config_test.go index 1873cfef8..b9d46f1c2 100644 --- a/core/config/model_config_test.go +++ b/core/config/model_config_test.go @@ -956,11 +956,11 @@ var _ = Describe("ModelConfig alias", func() { }) }) -var _ = Describe("systemone usecase", func() { - // A decision model never generates text, so a declared systemone list +var _ = Describe("decisions usecase", func() { + // A decision model never generates text, so a declared decisions list // must stay authoritative and the heuristic must never guess the flag. It("is authoritative when declared and never guessed", func() { - declared := GetUsecasesFromYAML([]string{"systemone"}) + declared := GetUsecasesFromYAML([]string{"decisions"}) Expect(declared).NotTo(BeNil()) Expect(*declared).NotTo(Equal(FLAG_ANY)) @@ -984,7 +984,7 @@ var _ = Describe("systemone usecase", func() { }) It("is a reserved usecase for the GGUF importer chat-default guard", func() { - declared := GetUsecasesFromYAML([]string{"systemone"}) + declared := GetUsecasesFromYAML([]string{"decisions"}) Expect(reservedNonChatModel(&ModelConfig{Backend: "vllm-cpp", KnownUsecases: declared})).To(BeTrue()) }) }) diff --git a/core/gallery/vllm_cpp_tags_test.go b/core/gallery/vllm_cpp_tags_test.go index 799dd471e..b84f9a2b1 100644 --- a/core/gallery/vllm_cpp_tags_test.go +++ b/core/gallery/vllm_cpp_tags_test.go @@ -19,7 +19,7 @@ var _ = Describe("gallery/index.yaml vllm-cpp capability tags", func() { Expect(err).ToNot(HaveOccurred()) tagToFlag := map[string]config.ModelConfigUsecase{ - "systemone": config.FLAG_SYSTEMONE, + "decisions": config.FLAG_DECISIONS, "vision": config.FLAG_VISION, "token-classify": config.FLAG_TOKEN_CLASSIFY, "scoring": config.FLAG_SCORE, diff --git a/core/http/auth/features.go b/core/http/auth/features.go index 94ae73f4d..4c1f53ec2 100644 --- a/core/http/auth/features.go +++ b/core/http/auth/features.go @@ -71,10 +71,10 @@ var RouteFeatureRegistry = []RouteFeature{ // Detection {"POST", "/v1/detection", FeatureDetection}, - // SystemOne decision API - {"POST", "/v1/systemone", FeatureSystemOne}, - {"POST", "/v1/systemone/permute", FeatureSystemOne}, - {"POST", "/v1/systemone/separate", FeatureSystemOne}, + // Decisions API (SystemOne wire contract) + {"POST", "/v1/systemone", FeatureDecisions}, + {"POST", "/v1/systemone/permute", FeatureDecisions}, + {"POST", "/v1/systemone/separate", FeatureDecisions}, // Face recognition {"POST", "/v1/face/verify", FeatureFaceRecognition}, @@ -214,6 +214,6 @@ func APIFeatureMetas() []FeatureMeta { {FeatureVoiceRecognition, "Voice Recognition", true}, {FeatureAudioTransform, "Audio Transform", true}, {FeaturePIIFilter, "PII Analyze / Redact", true}, - {FeatureSystemOne, "SystemOne Decisions", true}, + {FeatureDecisions, "Decisions", true}, } } diff --git a/core/http/auth/features_systemone_test.go b/core/http/auth/features_decisions_test.go similarity index 70% rename from core/http/auth/features_systemone_test.go rename to core/http/auth/features_decisions_test.go index 31cfc6cb7..4c5528618 100644 --- a/core/http/auth/features_systemone_test.go +++ b/core/http/auth/features_decisions_test.go @@ -6,19 +6,19 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("SystemOne feature registration", func() { +var _ = Describe("Decisions feature registration", func() { It("gates the three decision routes behind one default-on API feature", func() { - Expect(APIFeatures).To(ContainElement(FeatureSystemOne)) + Expect(APIFeatures).To(ContainElement(FeatureDecisions)) patterns := []string{} for _, route := range RouteFeatureRegistry { - if route.Feature == FeatureSystemOne { + if route.Feature == FeatureDecisions { Expect(route.Method).To(Equal("POST")) patterns = append(patterns, route.Pattern) } } Expect(patterns).To(ConsistOf("/v1/systemone", "/v1/systemone/permute", "/v1/systemone/separate")) - Expect(APIFeatureMetas()).To(ContainElement(FeatureMeta{Key: FeatureSystemOne, Label: "SystemOne Decisions", DefaultValue: true})) + Expect(APIFeatureMetas()).To(ContainElement(FeatureMeta{Key: FeatureDecisions, Label: "Decisions", DefaultValue: true})) }) }) diff --git a/core/http/auth/permissions.go b/core/http/auth/permissions.go index c01f6e72b..3f8b6deff 100644 --- a/core/http/auth/permissions.go +++ b/core/http/auth/permissions.go @@ -59,7 +59,7 @@ const ( FeatureFaceRecognition = "face_recognition" FeatureVoiceRecognition = "voice_recognition" FeatureAudioTransform = "audio_transform" - FeatureSystemOne = "systemone" + FeatureDecisions = "decisions" // FeaturePIIFilter gates the synchronous PII analyze/redact service // (POST /api/pii/{analyze,redact}). Default ON like the other API // features; the admin-only events log is gated separately in-handler. @@ -79,7 +79,7 @@ var APIFeatures = []string{ FeatureVAD, FeatureDetection, FeatureVideo, Feature3D, FeatureEmbeddings, FeatureSound, FeatureRealtime, FeatureModeration, FeatureRerank, FeatureTokenize, FeatureMCP, FeatureStores, FeatureFaceRecognition, FeatureVoiceRecognition, FeatureAudioTransform, - FeaturePIIFilter, FeatureSystemOne, + FeaturePIIFilter, FeatureDecisions, } // AllFeatures lists all known features (used by UI and validation). diff --git a/core/http/endpoints/localai/api_instructions.go b/core/http/endpoints/localai/api_instructions.go index 702c771ff..107f4fe59 100644 --- a/core/http/endpoints/localai/api_instructions.go +++ b/core/http/endpoints/localai/api_instructions.go @@ -106,10 +106,10 @@ var instructionDefs = []instructionDef{ Intro: "Voice (speaker) recognition — the audio analog to /v1/face/*. Use /v1/voice/verify for 1:1 speaker comparison, /v1/voice/identify for 1:N match against the registered store, /v1/voice/{register,forget} to manage that store, /v1/voice/embed for a raw speaker-encoder vector, and /v1/voice/analyze for age / gender / emotion inferred from speech. Registrations are in-memory by default and lost on restart. Audio inputs accept URL, base64, or data-URI; /v1/embeddings remains text-only.", }, { - Name: "systemone", + Name: "decisions", Description: "Typed decisions (choice, noul, score) over a state text with calibrated confidence", Tags: []string{"systemone"}, - Intro: "POST /v1/systemone answers every question in one pass; /v1/systemone/permute re-runs one choice question under n_perm option orders; /v1/systemone/separate answers each question in its own pass. Request: { model, state, questions: { : { type: choice|noul|score, instructions, criteria } } }. A decision model declares known_usecases: [systemone] and serves only /v1/systemone; a zero-shot NER model declares token_classify and serves all three routes (through the NER path); /permute and /separate return 400 for decision models. A vllm-cpp config that declares no usecases is treated as a decision model. Responses carry per-question answers with confidence and probabilities plus token usage. The wire shape matches Ollama's /v1/systemone.", + Intro: "POST /v1/systemone answers every question in one pass; /v1/systemone/permute re-runs one choice question under n_perm option orders; /v1/systemone/separate answers each question in its own pass. Request: { model, state, questions: { : { type: choice|noul|score, instructions, criteria } } }. A decision model declares known_usecases: [decisions] and serves only /v1/systemone; a zero-shot NER model declares token_classify and serves all three routes (through the NER path); /permute and /separate return 400 for decision models. A vllm-cpp config that declares no usecases is treated as a decision model. Responses carry per-question answers with confidence and probabilities plus token usage. The wire shape matches Ollama's /v1/systemone.", }, { Name: "branding", diff --git a/core/http/endpoints/localai/api_instructions_test.go b/core/http/endpoints/localai/api_instructions_test.go index 727f4cb86..a3b504304 100644 --- a/core/http/endpoints/localai/api_instructions_test.go +++ b/core/http/endpoints/localai/api_instructions_test.go @@ -82,7 +82,7 @@ var _ = Describe("API Instructions Endpoints", func() { "voice-library", "3d", "failover", - "systemone", + "decisions", )) }) }) @@ -137,15 +137,15 @@ var _ = Describe("API Instructions Endpoints", func() { Expect(string(body)).NotTo(ContainSubstring("/v1/3d/generations")) }) - It("should advertise the SystemOne decisions API", func() { - req := httptest.NewRequest(http.MethodGet, "/api/instructions/systemone", nil) + It("should advertise the Decisions API", func() { + req := httptest.NewRequest(http.MethodGet, "/api/instructions/decisions", nil) rec := httptest.NewRecorder() app.ServeHTTP(rec, req) Expect(rec.Code).To(Equal(http.StatusOK)) body, _ := io.ReadAll(rec.Body) Expect(string(body)).To(ContainSubstring("POST /v1/systemone")) - Expect(string(body)).To(ContainSubstring("known_usecases: [systemone]")) + Expect(string(body)).To(ContainSubstring("known_usecases: [decisions]")) }) It("should return JSON fragment when format=json", func() { diff --git a/core/http/endpoints/localai/systemone.go b/core/http/endpoints/localai/systemone.go index 17f68a71a..64ba78e1f 100644 --- a/core/http/endpoints/localai/systemone.go +++ b/core/http/endpoints/localai/systemone.go @@ -379,10 +379,10 @@ func systemOneModelAllowed(cfg config.ModelConfig) error { if cfg.KnownUsecases == nil { return nil } - if *cfg.KnownUsecases&(config.FLAG_SYSTEMONE|config.FLAG_TOKEN_CLASSIFY) != 0 { + if *cfg.KnownUsecases&(config.FLAG_DECISIONS|config.FLAG_TOKEN_CLASSIFY) != 0 { return nil } - return fmt.Errorf("model %q does not declare the systemone usecase (known_usecases: [systemone])", cfg.Name) + return fmt.Errorf("model %q does not declare the decisions usecase (known_usecases: [decisions])", cfg.Name) } // checkSystemOneModel applies systemOneModelAllowed to a model looked up by @@ -405,7 +405,7 @@ func checkSystemOneModel(app *application.Application, modelName string) error { // A model that declares token_classify without systemone is a zero-shot NER // model: the backend's decision entry point refuses those architectures, so it // goes to the NER path instead. A config that declares nothing keeps the -// decision pipeline, which is what setups that predate the systemone usecase +// decision pipeline, which is what setups that predate the decisions usecase // relied on. func systemOneUsesDecisionPipeline(cfg config.ModelConfig) bool { if !backendSupportsScore(cfg.Backend) { @@ -415,7 +415,7 @@ func systemOneUsesDecisionPipeline(cfg config.ModelConfig) bool { return true } declared := *cfg.KnownUsecases - if declared&config.FLAG_SYSTEMONE != 0 { + if declared&config.FLAG_DECISIONS != 0 { return true } return declared&config.FLAG_TOKEN_CLASSIFY == 0 @@ -429,7 +429,7 @@ func systemOneNERAllowed(cfg config.ModelConfig) error { return nil } declared := *cfg.KnownUsecases - if declared&config.FLAG_SYSTEMONE != 0 && declared&config.FLAG_TOKEN_CLASSIFY == 0 { + if declared&config.FLAG_DECISIONS != 0 && declared&config.FLAG_TOKEN_CLASSIFY == 0 { return fmt.Errorf("model %q is a decision model: /permute and /separate use the NER path, use POST /v1/systemone instead", cfg.Name) } return nil diff --git a/core/http/endpoints/localai/systemone_gate_test.go b/core/http/endpoints/localai/systemone_gate_test.go index b790c8df2..65978c463 100644 --- a/core/http/endpoints/localai/systemone_gate_test.go +++ b/core/http/endpoints/localai/systemone_gate_test.go @@ -16,8 +16,8 @@ var _ = Describe("systemOneModelAllowed", func() { } } - It("accepts a declared systemone model", func() { - Expect(systemOneModelAllowed(mk("systemone"))).To(Succeed()) + It("accepts a declared decisions model", func() { + Expect(systemOneModelAllowed(mk("decisions"))).To(Succeed()) }) It("accepts a token_classify model, which the NER path serves", func() { @@ -29,7 +29,7 @@ var _ = Describe("systemOneModelAllowed", func() { }) It("refuses a chat-only model with an actionable message", func() { - Expect(systemOneModelAllowed(mk("chat"))).To(MatchError(ContainSubstring("known_usecases: [systemone]"))) + Expect(systemOneModelAllowed(mk("chat"))).To(MatchError(ContainSubstring("known_usecases: [decisions]"))) }) }) @@ -44,7 +44,7 @@ var _ = Describe("systemone routing by model kind", func() { Describe("systemOneUsesDecisionPipeline", func() { It("sends a declared decision model to the decision pipeline", func() { - Expect(systemOneUsesDecisionPipeline(mk("vllm-cpp", "systemone"))).To(BeTrue()) + Expect(systemOneUsesDecisionPipeline(mk("vllm-cpp", "decisions"))).To(BeTrue()) }) It("sends a token_classify model to the NER path, since vllm_decide refuses NER architectures", func() { Expect(systemOneUsesDecisionPipeline(mk("vllm-cpp", "token_classify"))).To(BeFalse()) @@ -53,16 +53,16 @@ var _ = Describe("systemone routing by model kind", func() { Expect(systemOneUsesDecisionPipeline(mk("vllm-cpp"))).To(BeTrue()) }) It("prefers the decision pipeline when both usecases are declared", func() { - Expect(systemOneUsesDecisionPipeline(mk("vllm-cpp", "systemone", "token_classify"))).To(BeTrue()) + Expect(systemOneUsesDecisionPipeline(mk("vllm-cpp", "decisions", "token_classify"))).To(BeTrue()) }) It("never uses it for a backend without the Score RPC", func() { - Expect(systemOneUsesDecisionPipeline(mk("no-such-backend", "systemone"))).To(BeFalse()) + Expect(systemOneUsesDecisionPipeline(mk("no-such-backend", "decisions"))).To(BeFalse()) }) }) Describe("systemOneNERAllowed", func() { It("refuses a decision model on the NER-only routes with an actionable message", func() { - Expect(systemOneNERAllowed(mk("vllm-cpp", "systemone"))).To(MatchError(ContainSubstring("/v1/systemone"))) + Expect(systemOneNERAllowed(mk("vllm-cpp", "decisions"))).To(MatchError(ContainSubstring("/v1/systemone"))) }) It("accepts a token_classify model", func() { Expect(systemOneNERAllowed(mk("vllm-cpp", "token_classify"))).To(Succeed()) diff --git a/core/http/react-ui/e2e/models-lifecycle.spec.js b/core/http/react-ui/e2e/models-lifecycle.spec.js index 6587bff8b..0dff49857 100644 --- a/core/http/react-ui/e2e/models-lifecycle.spec.js +++ b/core/http/react-ui/e2e/models-lifecycle.spec.js @@ -172,17 +172,17 @@ test.describe('Models lifecycle', () => { await expect(installedPane(page)).toContainText('Worker one') }) - test('shows the systemone use case on a decision model', async ({ page }) => { + test('shows the decisions use case on a decision model', async ({ page }) => { await page.route('**/api/models/capabilities', route => route.fulfill({ contentType: 'application/json', body: JSON.stringify({ - data: [...installedModels, { id: 'decider', backend: 'vllm-cpp', capabilities: ['FLAG_SYSTEMONE'] }], + data: [...installedModels, { id: 'decider', backend: 'vllm-cpp', capabilities: ['FLAG_DECISIONS'] }], }), })) await page.goto('/app/models?view=installed&model=decider') await expect(installedPane(page)).toContainText('decider') - await expect(installedPane(page)).toContainText('SystemOne') + await expect(installedPane(page)).toContainText('Decisions') }) test('stops a running model with confirmation', async ({ page }) => { diff --git a/core/http/react-ui/public/locales/de/models.json b/core/http/react-ui/public/locales/de/models.json index d5c58a6b9..af487c86e 100644 --- a/core/http/react-ui/public/locales/de/models.json +++ b/core/http/react-ui/public/locales/de/models.json @@ -46,7 +46,7 @@ "open": { "title": "Open", "chat": "Chat", "completion": "Completion", "image": "Image", "video": "Video", "tts": "TTS", "transcribe": "Transcribe", "sound": "Sound", "face": "Face", "voice": "Voice", "embeddings": "Embeddings", - "rerank": "Rerank", "vad": "VAD", "score": "Score", "systemone": "SystemOne" + "rerank": "Rerank", "vad": "VAD", "score": "Score", "decisions": "Decisions" }, "empty": { "title": "No models installed yet", "text": "Explore the gallery or import a model to get started.", diff --git a/core/http/react-ui/public/locales/en/models.json b/core/http/react-ui/public/locales/en/models.json index b5ab38303..f60dc3051 100644 --- a/core/http/react-ui/public/locales/en/models.json +++ b/core/http/react-ui/public/locales/en/models.json @@ -46,7 +46,7 @@ "open": { "title": "Open", "chat": "Chat", "completion": "Completion", "image": "Image", "video": "Video", "tts": "TTS", "transcribe": "Transcribe", "sound": "Sound", "face": "Face", "voice": "Voice", "embeddings": "Embeddings", - "rerank": "Rerank", "vad": "VAD", "score": "Score", "systemone": "SystemOne" + "rerank": "Rerank", "vad": "VAD", "score": "Score", "decisions": "Decisions" }, "empty": { "title": "No models installed yet", "text": "Explore the gallery or import a model to get started.", diff --git a/core/http/react-ui/public/locales/es/models.json b/core/http/react-ui/public/locales/es/models.json index 27fc03752..eddf6a0b4 100644 --- a/core/http/react-ui/public/locales/es/models.json +++ b/core/http/react-ui/public/locales/es/models.json @@ -46,7 +46,7 @@ "open": { "title": "Open", "chat": "Chat", "completion": "Completion", "image": "Image", "video": "Video", "tts": "TTS", "transcribe": "Transcribe", "sound": "Sound", "face": "Face", "voice": "Voice", "embeddings": "Embeddings", - "rerank": "Rerank", "vad": "VAD", "score": "Score", "systemone": "SystemOne" + "rerank": "Rerank", "vad": "VAD", "score": "Score", "decisions": "Decisions" }, "empty": { "title": "No models installed yet", "text": "Explore the gallery or import a model to get started.", diff --git a/core/http/react-ui/public/locales/id/models.json b/core/http/react-ui/public/locales/id/models.json index 67ea168f7..4ca0bcd2a 100644 --- a/core/http/react-ui/public/locales/id/models.json +++ b/core/http/react-ui/public/locales/id/models.json @@ -46,7 +46,7 @@ "open": { "title": "Open", "chat": "Chat", "completion": "Completion", "image": "Image", "video": "Video", "tts": "TTS", "transcribe": "Transcribe", "sound": "Sound", "face": "Face", "voice": "Voice", "embeddings": "Embeddings", - "rerank": "Rerank", "vad": "VAD", "score": "Score", "systemone": "SystemOne" + "rerank": "Rerank", "vad": "VAD", "score": "Score", "decisions": "Decisions" }, "empty": { "title": "No models installed yet", "text": "Explore the gallery or import a model to get started.", diff --git a/core/http/react-ui/public/locales/it/models.json b/core/http/react-ui/public/locales/it/models.json index 0f4c80e56..b67d9da75 100644 --- a/core/http/react-ui/public/locales/it/models.json +++ b/core/http/react-ui/public/locales/it/models.json @@ -46,7 +46,7 @@ "open": { "title": "Open", "chat": "Chat", "completion": "Completion", "image": "Image", "video": "Video", "tts": "TTS", "transcribe": "Transcribe", "sound": "Sound", "face": "Face", "voice": "Voice", "embeddings": "Embeddings", - "rerank": "Rerank", "vad": "VAD", "score": "Score", "systemone": "SystemOne" + "rerank": "Rerank", "vad": "VAD", "score": "Score", "decisions": "Decisions" }, "empty": { "title": "No models installed yet", "text": "Explore the gallery or import a model to get started.", diff --git a/core/http/react-ui/public/locales/ko/models.json b/core/http/react-ui/public/locales/ko/models.json index 74874ed70..e47a7da87 100644 --- a/core/http/react-ui/public/locales/ko/models.json +++ b/core/http/react-ui/public/locales/ko/models.json @@ -46,7 +46,7 @@ "open": { "title": "Open", "chat": "Chat", "completion": "Completion", "image": "Image", "video": "Video", "tts": "TTS", "transcribe": "Transcribe", "sound": "Sound", "face": "Face", "voice": "Voice", "embeddings": "Embeddings", - "rerank": "Rerank", "vad": "VAD", "score": "Score", "systemone": "SystemOne" + "rerank": "Rerank", "vad": "VAD", "score": "Score", "decisions": "Decisions" }, "empty": { "title": "No models installed yet", "text": "Explore the gallery or import a model to get started.", diff --git a/core/http/react-ui/public/locales/pt-BR/models.json b/core/http/react-ui/public/locales/pt-BR/models.json index 8a795411e..3f0e5c97a 100644 --- a/core/http/react-ui/public/locales/pt-BR/models.json +++ b/core/http/react-ui/public/locales/pt-BR/models.json @@ -46,7 +46,7 @@ "open": { "title": "Open", "chat": "Chat", "completion": "Completion", "image": "Image", "video": "Video", "tts": "TTS", "transcribe": "Transcribe", "sound": "Sound", "face": "Face", "voice": "Voice", "embeddings": "Embeddings", - "rerank": "Rerank", "vad": "VAD", "score": "Score", "systemone": "SystemOne" + "rerank": "Rerank", "vad": "VAD", "score": "Score", "decisions": "Decisions" }, "empty": { "title": "No models installed yet", "text": "Explore the gallery or import a model to get started.", diff --git a/core/http/react-ui/public/locales/zh-CN/models.json b/core/http/react-ui/public/locales/zh-CN/models.json index e130ff78a..524e49933 100644 --- a/core/http/react-ui/public/locales/zh-CN/models.json +++ b/core/http/react-ui/public/locales/zh-CN/models.json @@ -46,7 +46,7 @@ "open": { "title": "Open", "chat": "Chat", "completion": "Completion", "image": "Image", "video": "Video", "tts": "TTS", "transcribe": "Transcribe", "sound": "Sound", "face": "Face", "voice": "Voice", "embeddings": "Embeddings", - "rerank": "Rerank", "vad": "VAD", "score": "Score", "systemone": "SystemOne" + "rerank": "Rerank", "vad": "VAD", "score": "Score", "decisions": "Decisions" }, "empty": { "title": "No models installed yet", "text": "Explore the gallery or import a model to get started.", diff --git a/core/http/react-ui/src/pages/InstalledModels.jsx b/core/http/react-ui/src/pages/InstalledModels.jsx index c607a61ac..fce53a124 100644 --- a/core/http/react-ui/src/pages/InstalledModels.jsx +++ b/core/http/react-ui/src/pages/InstalledModels.jsx @@ -22,7 +22,7 @@ import { CAP_CHAT, CAP_COMPLETION, CAP_IMAGE, CAP_VIDEO, CAP_TTS, CAP_TRANSCRIPT, CAP_SOUND_GENERATION, CAP_FACE_RECOGNITION, CAP_SPEAKER_RECOGNITION, CAP_EMBEDDINGS, CAP_RERANK, - CAP_VAD, CAP_SCORE, CAP_SYSTEMONE, + CAP_VAD, CAP_SCORE, CAP_DECISIONS, } from '../utils/capabilities' const USE_CASES = [ @@ -39,7 +39,7 @@ const USE_CASES = [ { cap: CAP_RERANK, labelKey: 'rerank' }, { cap: CAP_VAD, labelKey: 'vad' }, { cap: CAP_SCORE, labelKey: 'score' }, - { cap: CAP_SYSTEMONE, labelKey: 'systemone' }, + { cap: CAP_DECISIONS, labelKey: 'decisions' }, ] export function modelUseCases(model) { diff --git a/core/http/react-ui/src/utils/capabilities.js b/core/http/react-ui/src/utils/capabilities.js index 0775ef8d9..722c85842 100644 --- a/core/http/react-ui/src/utils/capabilities.js +++ b/core/http/react-ui/src/utils/capabilities.js @@ -29,5 +29,5 @@ export const CAP_SPEAKER_RECOGNITION = 'FLAG_SPEAKER_RECOGNITION' export const CAP_AUDIO_TRANSFORM = 'FLAG_AUDIO_TRANSFORM' export const CAP_REALTIME_AUDIO = 'FLAG_REALTIME_AUDIO' export const CAP_SCORE = 'FLAG_SCORE' -export const CAP_SYSTEMONE = 'FLAG_SYSTEMONE' +export const CAP_DECISIONS = 'FLAG_DECISIONS' export const CAP_TOKEN_CLASSIFY = 'FLAG_TOKEN_CLASSIFY' diff --git a/docs/content/advanced/model-configuration.md b/docs/content/advanced/model-configuration.md index 10e71ef96..c1870e3ec 100644 --- a/docs/content/advanced/model-configuration.md +++ b/docs/content/advanced/model-configuration.md @@ -1066,9 +1066,9 @@ known_usecases: - embeddings ``` -Available flags: `chat`, `completion`, `edit`, `embeddings`, `rerank`, `image`, `transcript`, `tts`, `sound_generation`, `tokenize`, `vad`, `video`, `detection`, `score`, `token_classify`, `systemone`, `llm` (combination of CHAT, COMPLETION, EDIT). +Available flags: `chat`, `completion`, `edit`, `embeddings`, `rerank`, `image`, `transcript`, `tts`, `sound_generation`, `tokenize`, `vad`, `video`, `detection`, `score`, `token_classify`, `decisions`, `llm` (combination of CHAT, COMPLETION, EDIT). -`systemone` marks a model as a decision model for the [SystemOne API]({{% relref "features/systemone" %}}) (`POST /v1/systemone`). It is never guessed, and a model that declares it is not listed as a chat, completion or embeddings model. +`decisions` marks a model as a decision model for the [Decisions API]({{% relref "features/decisions" %}}) (`POST /v1/systemone`). It is never guessed, and a model that declares it is not listed as a chat, completion or embeddings model. `token_classify` marks a model as a token-classification (NER) provider for the PII filter (e.g. an `openai-privacy-filter` GGUF). Declare it explicitly together with `embeddings: true` (the classifier loads via TOKEN_CLS pooling). It runs on the dedicated `privacy-filter` backend (`backend/cpp/privacy-filter`), a standalone GGML engine for the `openai-privacy-filter` family - separate from `llama-cpp`, which no longer carries the token-classification path. diff --git a/docs/content/features/systemone.md b/docs/content/features/decisions.md similarity index 78% rename from docs/content/features/systemone.md rename to docs/content/features/decisions.md index 84c65a665..fa3422aaa 100644 --- a/docs/content/features/systemone.md +++ b/docs/content/features/decisions.md @@ -1,17 +1,19 @@ +++ disableToc = false -title = "SystemOne decisions" +title = "Decisions API" weight = 66 -url = "/features/systemone/" +url = "/features/decisions/" +++ -SystemOne is an API for fast, typed decisions. You send a piece of text (the +The Decisions API is a fast, typed decision layer. You send a piece of text (the *state*) and a set of named questions. A decision model answers each question with a value and a confidence, in one pass. The model does not generate text, so there is nothing to parse and no free-form output to validate. -The request and response shapes follow the [kev](https://github.com/jaredpalmer/kev) -project and match the `/v1/systemone` endpoint that Ollama added in 0.35. +LocalAI serves it on the `/v1/systemone` routes. The request and response shapes +follow the [kev](https://github.com/jaredpalmer/kev) project and match the +`/v1/systemone` endpoint that Ollama added in 0.35. The wire contract is called +SystemOne; the capability a model declares is called `decisions`. ## Endpoints @@ -25,7 +27,7 @@ Which route a model can serve depends on its kind: | Model kind | `/v1/systemone` | `/permute` and `/separate` | |---|---|---| -| Decision model (`systemone`), such as Laya or GLiNER2.5-Decide | Yes | No, returns `400` | +| Decision model (`decisions`), such as Laya or GLiNER2.5-Decide | Yes | No, returns `400` | | Zero-shot NER model (`token_classify`), such as GLiNER2.5 | Yes, through the NER path | Yes | ## Question types @@ -70,28 +72,28 @@ reports token usage and `latency_ms`. The NER path does not report token usage. ## Choosing a model -A model can serve SystemOne only if it is a decision model. Declare the usecase +A model can serve the Decisions API only if it is a decision model. Declare the usecase in the model config: ```yaml name: laya backend: vllm-cpp known_usecases: - - systemone + - decisions parameters: model: convaiinnovations/laya ``` -`systemone` is never guessed, and a model that declares it is not listed as a +`decisions` is never guessed, and a model that declares it is not listed as a chat, completion or embeddings model. A model that declares usecases without -`systemone` or `token_classify` gets a `400` from these endpoints that names the -missing usecase. A model that declares `token_classify` and not `systemone` is +`decisions` or `token_classify` gets a `400` from these endpoints that names the +missing usecase. A model that declares `token_classify` and not `decisions` is served by the zero-shot NER path. A vllm-cpp config that declares no usecases is treated as a decision model, so setups that predate the flag keep working, but a config that declares only `chat` (as an older `laya` gallery entry did) now gets -the `400` and needs `known_usecases: [systemone]`. +the `400` and needs `known_usecases: [decisions]`. -Install one from the gallery and filter on the `systemone` tag: +Install one from the gallery and filter on the `decisions` tag: | Gallery entry | Model | Notes | |---|---|---| @@ -107,6 +109,6 @@ and does not serve `/v1/systemone` yet. ## Access control -When authentication is on, the three routes need the `systemone` feature. It is +When authentication is on, the three routes need the `decisions` feature. It is on by default for every user, like the other API features, and an administrator can turn it off per user. diff --git a/docs/content/features/vllm-cpp.md b/docs/content/features/vllm-cpp.md index ba9840302..bf7323e0d 100644 --- a/docs/content/features/vllm-cpp.md +++ b/docs/content/features/vllm-cpp.md @@ -160,12 +160,12 @@ forward, which is the required contract for pooling models in vllm.cpp. A device-resident forward is tracked as a performance optimization, not a correctness gap. -### SystemOne decision API +### Decisions API -The `vllm-cpp` backend serves the kev-compatible SystemOne endpoints: typed +The `vllm-cpp` backend serves the kev-compatible SystemOne endpoints (the Decisions API): typed `choice`, `noul` and `score` questions over a state text, answered by a non-generative decision model in one pass. A decision model declares -`known_usecases: [systemone]`. See [SystemOne decisions]({{% relref "features/systemone" %}}) +`known_usecases: [decisions]`. See [Decisions API]({{% relref "features/decisions" %}}) for the request shape, the models you can install and the access rules. | Endpoint | Method | Description | diff --git a/gallery/index.yaml b/gallery/index.yaml index 10276000a..5bded4aaf 100644 --- a/gallery/index.yaml +++ b/gallery/index.yaml @@ -63653,7 +63653,7 @@ 512-token context. F16 weights, ~804 MB. license: apache-2.0 tags: - - decision + - decisions - systemone - vllm-cpp - cpu @@ -63663,7 +63663,7 @@ overrides: backend: vllm-cpp known_usecases: - - systemone + - decisions parameters: model: convaiinnovations/laya artifacts: @@ -63690,7 +63690,7 @@ checkpoint it was checked against. license: apache-2.0 tags: - - decision + - decisions - systemone - vllm-cpp - cpu @@ -63700,7 +63700,7 @@ overrides: backend: vllm-cpp known_usecases: - - systemone + - decisions parameters: model: fastino/GLiNER2.5-Decide artifacts: