mirror of
https://github.com/mudler/LocalAI.git
synced 2026-06-18 21:58:58 -04:00
Squashed feat/pii-ner-tier-engine rebased onto master (was 45 commits; see backup/pii-ner-tier-engine-prerebase). Net change: - privacy-filter.cpp: standalone GGML engine for the openai-privacy-filter PII/NER token classifier, wired as a LocalAI gRPC backend (CPU/CUDA/Vulkan). TokenClassify moves off the patched llama.cpp path onto this backend. - PII filter reworked to be NER-centric (encoder/NER detection tier scanning whole conversations as one document), with a recreated bounded restricted- regex secret-matching pattern detector tier alongside it (per-model pii_detection.builtins / .patterns + core/services/routing/piipattern). - Detection labelled by source (ner vs pattern); backend trace / confidence / debug observability; analyze/redact exposed as a synchronous API. - Instance-wide default detector policy + per-usecase default-on; request filtering extended to completions, embeddings, edits & Ollama. - React UI: NER-centric PII editor, detector-models table, pattern/builtins editor, middleware default-policy UI. - Gallery: privacy-filter-multilingual token-classify model + NER install filter; token_classify known_usecase; batch sized to context for NER models. privacy-filter backend registered in the backend gallery (cpu/vulkan/cuda-13 meta + image entries with a capabilities map) matching its CI matrix jobs, and an /import-model auto-detect importer (PrivacyFilterImporter, narrow privacy-filter GGUF detection) replacing the prior pref-only registration. Reconciled against master's independent evolution: - Dropped master's PIIPatternOverrides feature (global-pattern runtime overrides + /api/pii/patterns API + runtime_settings.json persistence). The per-model NER + pattern-detector design supersedes it; it was built on the global redactor pattern set this branch replaced. - Reverted the llama.cpp Score carry-patch (0006-server-task-type-score): removed the patch and restored master's grpc-server.cpp Score RPC (direct llama_decode, slot-loop bypass) and LLAMA_VERSION pin, plus master's model_config validation forbidding score + chat/completion/embeddings on llama-cpp. token_classify is unaffected (it runs on the privacy-filter backend, not llama-cpp). Assisted-by: Claude:claude-opus-4-8 [Claude Code] Signed-off-by: Richard Palethorpe <io@richiejp.com>
96 lines
4.2 KiB
Go
96 lines
4.2 KiB
Go
package localaitools
|
|
|
|
import (
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
// toolToHTTPRoute is the canonical mapping between MCP tools and the
|
|
// LocalAI admin REST endpoints they wrap. The httpapi.Client MUST hit the
|
|
// listed route for the tool; the inproc.Client may bypass HTTP and call
|
|
// services directly, but the on-the-wire shape is documented here so the
|
|
// two sides stay aligned.
|
|
//
|
|
// Updating the map is REQUIRED when:
|
|
// - You add a Tool* constant (tools.go).
|
|
// - You change which REST endpoint the httpapi.Client calls.
|
|
//
|
|
// The TestToolHTTPRouteMappingComplete spec below FAILS until every Tool*
|
|
// is in the map. That is the drift detector — see
|
|
// .agents/localai-assistant-mcp.md for the contributor contract.
|
|
//
|
|
// "(none)" is a deliberate sentinel for tools whose data is not exposed
|
|
// over a single REST endpoint (e.g. system_info aggregates data the
|
|
// inproc client picks up directly from services). The httpapi.Client may
|
|
// approximate via the welcome JSON; the test still requires an entry so
|
|
// the contributor explicitly acknowledges the asymmetry.
|
|
var toolToHTTPRoute = map[string]string{
|
|
// Read-only tools.
|
|
ToolGallerySearch: "GET /models/available",
|
|
ToolListInstalledModels: "GET / (welcome JSON, ModelsConfig field)",
|
|
ToolListGalleries: "GET /models/galleries",
|
|
ToolGetJobStatus: "GET /models/jobs/:uuid",
|
|
ToolGetModelConfig: "(none) — no JSON-only REST yet; httpapi.Client returns a documented stub",
|
|
ToolListBackends: "GET /backends",
|
|
ToolListKnownBackends: "GET /backends/known",
|
|
ToolSystemInfo: "GET / (welcome JSON)",
|
|
ToolListNodes: "GET /api/nodes",
|
|
ToolVRAMEstimate: "POST /api/models/vram-estimate",
|
|
ToolGetBranding: "GET /api/branding",
|
|
ToolGetUsageStats: "GET /api/usage (or /api/usage/all when all=true)",
|
|
ToolGetPIIEvents: "GET /api/pii/events",
|
|
ToolGetMiddlewareStatus: "GET /api/middleware/status",
|
|
ToolGetRouterDecisions: "GET /api/router/decisions",
|
|
|
|
// Mutating tools.
|
|
ToolInstallModel: "POST /models/apply",
|
|
ToolImportModelURI: "POST /models/import-uri",
|
|
ToolDeleteModel: "POST /models/delete/:name",
|
|
ToolEditModelConfig: "PATCH /api/models/config-json/:name",
|
|
ToolReloadModels: "POST /models/reload",
|
|
ToolInstallBackend: "POST /backends/apply",
|
|
ToolUpgradeBackend: "POST /backends/upgrade/:name",
|
|
ToolToggleModelState: "PUT /models/toggle-state/:name/:action",
|
|
ToolToggleModelPinned: "PUT /models/toggle-pinned/:name/:action",
|
|
ToolSetBranding: "POST /api/settings (instance_name, instance_tagline)",
|
|
}
|
|
|
|
// allKnownTools is the union of expectedFullCatalog (defined in
|
|
// server_test.go). Keeping a single source of truth — the slice from
|
|
// server_test — and asserting the route map covers every entry catches
|
|
// the case "you added a Tool* but forgot to register it as MCP" indirectly
|
|
// (it'd be missing from expectedFullCatalog, which has its own assertion
|
|
// in TestServerRegistersExpectedToolCatalog).
|
|
var _ = Describe("Tool ↔ HTTP route coverage map", func() {
|
|
It("has an entry for every Tool* in the published catalog", func() {
|
|
for _, name := range expectedFullCatalog {
|
|
_, ok := toolToHTTPRoute[name]
|
|
Expect(ok).To(BeTrue(),
|
|
"Tool %q is in expectedFullCatalog but not in toolToHTTPRoute. "+
|
|
"When adding an MCP tool, update toolToHTTPRoute in coverage_test.go "+
|
|
"with the REST endpoint the httpapi.Client calls (or '(none)' with a reason).",
|
|
name)
|
|
}
|
|
})
|
|
|
|
It("does not document tools that no longer exist in the catalog", func() {
|
|
catalog := map[string]struct{}{}
|
|
for _, name := range expectedFullCatalog {
|
|
catalog[name] = struct{}{}
|
|
}
|
|
for name := range toolToHTTPRoute {
|
|
_, ok := catalog[name]
|
|
Expect(ok).To(BeTrue(),
|
|
"toolToHTTPRoute documents %q but the tool is not registered. "+
|
|
"Remove the stale entry.",
|
|
name)
|
|
}
|
|
})
|
|
|
|
// Deliberate non-test: we don't enumerate admin REST routes here. That
|
|
// would require booting Application or parsing core/http/routes/localai.go,
|
|
// both of which are brittle. The contract for "new admin REST endpoint
|
|
// → MCP tool" is enforced by the PR checklist in
|
|
// .agents/api-endpoints-and-auth.md, not by this test.
|
|
})
|