Files
LocalAI/core/http/auth/features.go
T
mudler-agentandEttore Di Giacinto 24a9feecbb feat(api): add OpenAI Decisions endpoint (#12603)
* feat: add OpenAI Decisions adapter with validated ordered answers

Share SystemOne execution and preserve ordered, typed Decisions answers. Reject null probabilities and derive scores from the emitted distribution. Cover conversion and handler contracts using a valid generated PNG fixture.

Assisted-by: nib:gpt-6-astra
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix: preserve Decisions choice types and empty score labels

Keep canonical JSON values in model-visible choice criteria and represent probability label presence explicitly. Add conversion and serialization regressions.

Assisted-by: nib:gpt-6-astra
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat: wire the OpenAI Decisions route

Expose the adapter through the shared Decisions feature gate and usage
middleware. Test real route registration with a fake backend, including
unauthenticated access, user permissions, and exact usage accounting.

Assisted-by: nib:gpt-6-astra
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* docs: document OpenAI-compatible decisions and discovery

Describe the shared local execution contract, typed questions, image limits, and backend accounting. Publish the Decisions route in Swagger and retain the existing SystemOne discovery tag. Use documented loose object placeholders for JSON unions that Swagger 2 cannot express.

Assisted-by: nib:gpt-6-astra
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

---------

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-10-10 08:29:31 +02:00

221 lines
8.1 KiB
Go

package auth
// RouteFeature maps a route pattern + HTTP method to a required feature.
type RouteFeature struct {
Method string // "POST", "GET", "*" (any)
Pattern string // Echo route pattern, e.g. "/v1/chat/completions"
Feature string // Feature constant, e.g. FeatureChat
}
// RouteFeatureRegistry is the single source of truth for endpoint -> feature mappings.
// To gate a new endpoint, add an entry here -- no other file changes needed.
var RouteFeatureRegistry = []RouteFeature{
// Chat / Completions
{"POST", "/v1/chat/completions", FeatureChat},
{"POST", "/chat/completions", FeatureChat},
{"POST", "/v1/completions", FeatureChat},
{"POST", "/completions", FeatureChat},
{"POST", "/v1/engines/:model/completions", FeatureChat},
{"POST", "/v1/edits", FeatureChat},
{"POST", "/edits", FeatureChat},
// Anthropic
{"POST", "/v1/messages", FeatureChat},
{"POST", "/messages", FeatureChat},
// Open Responses
{"POST", "/v1/responses", FeatureChat},
{"POST", "/responses", FeatureChat},
{"GET", "/v1/responses", FeatureChat},
{"GET", "/responses", FeatureChat},
// Embeddings
{"POST", "/v1/embeddings", FeatureEmbeddings},
{"POST", "/embeddings", FeatureEmbeddings},
{"POST", "/v1/engines/:model/embeddings", FeatureEmbeddings},
// Images
{"POST", "/v1/images/generations", FeatureImages},
{"POST", "/images/generations", FeatureImages},
{"POST", "/v1/images/inpainting", FeatureImages},
{"POST", "/images/inpainting", FeatureImages},
{"POST", "/v1/images/upscale", FeatureImages},
{"POST", "/images/upscale", FeatureImages},
// Audio transcription
{"POST", "/v1/audio/transcriptions", FeatureAudioTranscription},
{"POST", "/audio/transcriptions", FeatureAudioTranscription},
// Audio diarization (speaker turns)
{"POST", "/v1/audio/diarization", FeatureAudioDiarization},
{"POST", "/audio/diarization", FeatureAudioDiarization},
// Audio classification (sound-event tagging)
{"POST", "/v1/audio/classification", FeatureAudioClassification},
{"POST", "/audio/classification", FeatureAudioClassification},
// Audio speech / TTS
{"POST", "/v1/audio/speech", FeatureAudioSpeech},
{"POST", "/audio/speech", FeatureAudioSpeech},
{"POST", "/tts", FeatureAudioSpeech},
{"GET", "/v1/audio/voices", FeatureAudioSpeech},
{"GET", "/audio/voices", FeatureAudioSpeech},
{"POST", "/v1/text-to-speech/:voice-id", FeatureAudioSpeech},
{"GET", "/api/voice-profiles", FeatureAudioSpeech},
{"GET", "/api/voice-profiles/:id/audio", FeatureAudioSpeech},
// VAD
{"POST", "/vad", FeatureVAD},
{"POST", "/v1/vad", FeatureVAD},
// Detection
{"POST", "/v1/detection", FeatureDetection},
// Decisions API (OpenAI and SystemOne wire contracts)
{"POST", "/v1/decisions", FeatureDecisions},
{"POST", "/v1/systemone", FeatureDecisions},
{"POST", "/v1/systemone/permute", FeatureDecisions},
{"POST", "/v1/systemone/separate", FeatureDecisions},
// Face recognition
{"POST", "/v1/face/verify", FeatureFaceRecognition},
{"POST", "/v1/face/analyze", FeatureFaceRecognition},
{"POST", "/v1/face/embed", FeatureFaceRecognition},
{"POST", "/v1/face/register", FeatureFaceRecognition},
{"POST", "/v1/face/identify", FeatureFaceRecognition},
{"POST", "/v1/face/forget", FeatureFaceRecognition},
// Voice (speaker) recognition
{"POST", "/v1/voice/verify", FeatureVoiceRecognition},
{"POST", "/v1/voice/analyze", FeatureVoiceRecognition},
{"POST", "/v1/voice/embed", FeatureVoiceRecognition},
{"POST", "/v1/voice/register", FeatureVoiceRecognition},
{"POST", "/v1/voice/identify", FeatureVoiceRecognition},
{"POST", "/v1/voice/forget", FeatureVoiceRecognition},
// Audio transform (echo cancellation, noise suppression, voice conversion, etc.)
{"POST", "/audio/transformations", FeatureAudioTransform},
{"POST", "/audio/transform", FeatureAudioTransform},
{"GET", "/audio/transformations/stream", FeatureAudioTransform},
// Video
{"POST", "/video", FeatureVideo},
// 3D generation
{"POST", "/3d/generations", Feature3D},
{"POST", "/3d/remesh", Feature3D},
{"POST", "/3d/animate", Feature3D},
// Sound generation
{"POST", "/v1/sound-generation", FeatureSound},
// Realtime
{"GET", "/v1/realtime", FeatureRealtime},
{"POST", "/v1/realtime/sessions", FeatureRealtime},
{"POST", "/v1/realtime/transcription_session", FeatureRealtime},
{"POST", "/v1/realtime/calls", FeatureRealtime},
// MCP
{"POST", "/v1/mcp/chat/completions", FeatureMCP},
{"POST", "/mcp/v1/chat/completions", FeatureMCP},
{"POST", "/mcp/chat/completions", FeatureMCP},
// Tokenize
{"POST", "/v1/tokenize", FeatureTokenize},
{"POST", "/v1/detokenize", FeatureTokenize},
// Rerank
{"POST", "/v1/rerank", FeatureRerank},
// Moderation
{"POST", "/v1/moderations", FeatureModeration},
{"POST", "/moderations", FeatureModeration},
// Stores
{"POST", "/stores/set", FeatureStores},
{"POST", "/stores/delete", FeatureStores},
{"POST", "/stores/get", FeatureStores},
{"POST", "/stores/find", FeatureStores},
// Fine-tuning
{"POST", "/api/fine-tuning/jobs", FeatureFineTuning},
{"GET", "/api/fine-tuning/jobs", FeatureFineTuning},
{"GET", "/api/fine-tuning/jobs/:id", FeatureFineTuning},
{"POST", "/api/fine-tuning/jobs/:id/stop", FeatureFineTuning},
{"DELETE", "/api/fine-tuning/jobs/:id", FeatureFineTuning},
{"GET", "/api/fine-tuning/jobs/:id/progress", FeatureFineTuning},
{"GET", "/api/fine-tuning/jobs/:id/checkpoints", FeatureFineTuning},
{"POST", "/api/fine-tuning/jobs/:id/export", FeatureFineTuning},
{"GET", "/api/fine-tuning/jobs/:id/download", FeatureFineTuning},
{"POST", "/api/fine-tuning/datasets", FeatureFineTuning},
// PII analyze/redact service (the events log stays admin-gated in-handler)
{"POST", "/api/pii/analyze", FeaturePIIFilter},
{"POST", "/api/pii/redact", FeaturePIIFilter},
// Quantization
{"POST", "/api/quantization/jobs", FeatureQuantization},
{"GET", "/api/quantization/jobs", FeatureQuantization},
{"GET", "/api/quantization/jobs/:id", FeatureQuantization},
{"POST", "/api/quantization/jobs/:id/stop", FeatureQuantization},
{"DELETE", "/api/quantization/jobs/:id", FeatureQuantization},
{"GET", "/api/quantization/jobs/:id/progress", FeatureQuantization},
{"POST", "/api/quantization/jobs/:id/import", FeatureQuantization},
{"GET", "/api/quantization/jobs/:id/download", FeatureQuantization},
}
// FeatureMeta describes a feature for the admin API/UI.
type FeatureMeta struct {
Key string `json:"key"`
Label string `json:"label"`
DefaultValue bool `json:"default"`
}
// AgentFeatureMetas returns metadata for agent features.
func AgentFeatureMetas() []FeatureMeta {
return []FeatureMeta{
{FeatureAgents, "Agents", false},
{FeatureSkills, "Skills", false},
{FeatureCollections, "Collections", false},
{FeatureMCPJobs, "MCP CI Jobs", false},
{FeatureLocalAIAssistant, "LocalAI Assistant", false},
}
}
// GeneralFeatureMetas returns metadata for general features.
func GeneralFeatureMetas() []FeatureMeta {
return []FeatureMeta{
{FeatureFineTuning, "Fine-Tuning", false},
{FeatureQuantization, "Quantization", false},
}
}
// APIFeatureMetas returns metadata for API endpoint features.
func APIFeatureMetas() []FeatureMeta {
return []FeatureMeta{
{FeatureChat, "Chat Completions", true},
{FeatureImages, "Image Generation", true},
{FeatureAudioSpeech, "Audio Speech / TTS", true},
{FeatureAudioTranscription, "Audio Transcription", true},
{FeatureAudioDiarization, "Audio Diarization", true},
{FeatureAudioClassification, "Audio Classification", true},
{FeatureVAD, "Voice Activity Detection", true},
{FeatureDetection, "Detection", true},
{FeatureVideo, "Video Generation", true},
{Feature3D, "3D Generation", true},
{FeatureEmbeddings, "Embeddings", true},
{FeatureSound, "Sound Generation", true},
{FeatureRealtime, "Realtime", true},
{FeatureModeration, "Moderation", true},
{FeatureRerank, "Rerank", true},
{FeatureTokenize, "Tokenize", true},
{FeatureMCP, "MCP", true},
{FeatureStores, "Stores", true},
{FeatureFaceRecognition, "Face Recognition", true},
{FeatureVoiceRecognition, "Voice Recognition", true},
{FeatureAudioTransform, "Audio Transform", true},
{FeaturePIIFilter, "PII Analyze / Redact", true},
{FeatureDecisions, "Decisions", true},
}
}