From b4852d62d20a4b2489990b2c845e469c38214ff0 Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Wed, 30 Sep 2026 11:32:03 +0000 Subject: [PATCH] feat(systemone): register the decisions API on auth and instructions Adds a default-on systemone route feature for the three /v1/systemone routes and an /api/instructions area for them. No MCP tool is added: the endpoints run inference and are not admin install/edit actions, and the route-map test still passes. Assisted-by: Claude Code:claude-sonnet-5-5 Signed-off-by: Ettore Di Giacinto --- core/http/auth/features.go | 6 +++++ core/http/auth/features_systemone_test.go | 24 +++++++++++++++++++ core/http/auth/permissions.go | 3 ++- .../endpoints/localai/api_instructions.go | 6 +++++ .../localai/api_instructions_test.go | 14 ++++++++++- 5 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 core/http/auth/features_systemone_test.go diff --git a/core/http/auth/features.go b/core/http/auth/features.go index 45411f824..94ae73f4d 100644 --- a/core/http/auth/features.go +++ b/core/http/auth/features.go @@ -71,6 +71,11 @@ 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}, + // Face recognition {"POST", "/v1/face/verify", FeatureFaceRecognition}, {"POST", "/v1/face/analyze", FeatureFaceRecognition}, @@ -209,5 +214,6 @@ func APIFeatureMetas() []FeatureMeta { {FeatureVoiceRecognition, "Voice Recognition", true}, {FeatureAudioTransform, "Audio Transform", true}, {FeaturePIIFilter, "PII Analyze / Redact", true}, + {FeatureSystemOne, "SystemOne Decisions", true}, } } diff --git a/core/http/auth/features_systemone_test.go b/core/http/auth/features_systemone_test.go new file mode 100644 index 000000000..31cfc6cb7 --- /dev/null +++ b/core/http/auth/features_systemone_test.go @@ -0,0 +1,24 @@ +package auth_test + +import ( + . "github.com/mudler/LocalAI/core/http/auth" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("SystemOne feature registration", func() { + It("gates the three decision routes behind one default-on API feature", func() { + Expect(APIFeatures).To(ContainElement(FeatureSystemOne)) + + patterns := []string{} + for _, route := range RouteFeatureRegistry { + if route.Feature == FeatureSystemOne { + 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})) + }) +}) diff --git a/core/http/auth/permissions.go b/core/http/auth/permissions.go index 95e76f572..c01f6e72b 100644 --- a/core/http/auth/permissions.go +++ b/core/http/auth/permissions.go @@ -59,6 +59,7 @@ const ( FeatureFaceRecognition = "face_recognition" FeatureVoiceRecognition = "voice_recognition" FeatureAudioTransform = "audio_transform" + FeatureSystemOne = "systemone" // 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. @@ -78,7 +79,7 @@ var APIFeatures = []string{ FeatureVAD, FeatureDetection, FeatureVideo, Feature3D, FeatureEmbeddings, FeatureSound, FeatureRealtime, FeatureModeration, FeatureRerank, FeatureTokenize, FeatureMCP, FeatureStores, FeatureFaceRecognition, FeatureVoiceRecognition, FeatureAudioTransform, - FeaturePIIFilter, + FeaturePIIFilter, FeatureSystemOne, } // 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 8d0ea6d2f..dc60a3c21 100644 --- a/core/http/endpoints/localai/api_instructions.go +++ b/core/http/endpoints/localai/api_instructions.go @@ -105,6 +105,12 @@ var instructionDefs = []instructionDef{ Tags: []string{"voice-recognition"}, 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", + 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 } } }. The model must declare known_usecases: [systemone] (or token_classify for the zero-shot NER path); a config that declares no usecases keeps working. Responses carry per-question answers with confidence and probabilities plus token usage. The wire shape matches Ollama's /v1/systemone.", + }, { Name: "branding", Description: "Whitelabel the instance: configure name, tagline, logo, and favicon", diff --git a/core/http/endpoints/localai/api_instructions_test.go b/core/http/endpoints/localai/api_instructions_test.go index f42e1c92d..727f4cb86 100644 --- a/core/http/endpoints/localai/api_instructions_test.go +++ b/core/http/endpoints/localai/api_instructions_test.go @@ -39,7 +39,7 @@ var _ = Describe("API Instructions Endpoints", func() { instructions, ok := resp["instructions"].([]any) Expect(ok).To(BeTrue()) - Expect(instructions).To(HaveLen(20)) + Expect(instructions).To(HaveLen(21)) // Verify each instruction has required fields and correct URL format for _, s := range instructions { @@ -82,6 +82,7 @@ var _ = Describe("API Instructions Endpoints", func() { "voice-library", "3d", "failover", + "systemone", )) }) }) @@ -136,6 +137,17 @@ 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) + 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]")) + }) + It("should return JSON fragment when format=json", func() { req := httptest.NewRequest(http.MethodGet, "/api/instructions/chat-inference?format=json", nil) rec := httptest.NewRecorder()