Files
LocalAI/pkg/mcp/localaitools/httpapi/routes.go
LocalAI [bot] 8cec22c3b7 feat(vram): per-node VRAM allocation budget (LOCALAI_VRAM_BUDGET) (#10833)
* feat(vram): add vrambudget primitive for per-node VRAM caps

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(vram): apply default VRAM budget in xsysinfo aggregate getters

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(vram): wire LOCALAI_VRAM_BUDGET flag to xsysinfo default budget

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(vram): persist VRAM budget via runtime settings with live apply

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(vram): reset process-global VRAM budget after runtime-settings spec

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(vram): add VRAM budget field to Settings page

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(vram): store and enforce per-node VRAM budget in the node registry

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(vram): apply per-node VRAM budget in router hardware defaults

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(vram): report worker VRAM budget in node registration

The distributed worker now reports its operator-set VRAM budget string
(LOCALAI_VRAM_BUDGET) to the server on registration. The worker keeps
reporting RAW total/available VRAM and never sets the xsysinfo
process-global budget (that stays standalone-only); the server resolves
and enforces the budget uniformly (Task 6).

Also closes a Task 6 gap: on re-registration, a struct Updates zero-skips
an empty budget, so a worker that dropped LOCALAI_VRAM_BUDGET left the
stale cap in place. For non-admin-override nodes the budget columns are
now force-written (map Updates) even when empty, so removing the env var
clears the cap; admin overrides are preserved unchanged.

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* style(vram): drop em dash from worker-clear comment

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(vram): add node VRAM budget admin endpoints

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(vram): add node VRAM budget control to the node UI

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(vram): expose set_node_vram_budget MCP admin tool

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* docs(vram): document LOCALAI_VRAM_BUDGET and node VRAM budget UI

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(vram): avoid double-applying VRAM budget in GetResourceAggregateInfo

The GPU-branch aggregate returned by GetResourceInfo is sourced from
GetGPUAggregateInfo, which already caps total/free/used against the
process-wide VRAM budget. GetResourceAggregateInfo then applied the
budget a second time. For an absolute budget this is idempotent, but for
a percentage budget b.Apply resolves the ceiling as a fraction of its
input total, so a second pass yields P*(P*T) instead of P*T and distorts
UsagePercent (read by the memory reclaimer in pkg/model/watchdog.go).

Remove the redundant second application so the budget is applied exactly
once, against the raw physical totals, upstream in GetGPUAggregateInfo.

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(vram): implement SetNodeVRAMBudget on mcp assistant test stub

The LocalAIClient interface gained SetNodeVRAMBudget; the stubClient in
core/http/endpoints/mcp used by the assistant tests is a separate
implementer and needs the method too (broke golangci-lint typecheck and
both test jobs).

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-07-15 09:58:45 +02:00

69 lines
2.2 KiB
Go

package httpapi
import (
"fmt"
"net/url"
)
// Route paths for the LocalAI admin REST surface that this client targets.
// Static paths are constants; dynamic paths are builders that handle
// url.PathEscape on segment values. Keep these aligned with the server-side
// registrations in core/http/routes/localai.go — the Tool↔REST drift detector
// in coverage_test.go documents the mapping.
const (
routeWelcome = "/"
routeModelsApply = "/models/apply"
routeModelsAvail = "/models/available"
routeModelsGall = "/models/galleries"
routeModelsImport = "/models/import-uri"
routeModelImport = "/models/import"
routeAliases = "/api/aliases"
routeModelsReload = "/models/reload"
routeBackendLoad = "/backend/load"
routeBackends = "/backends"
routeBackendsKnown = "/backends/known"
routeBackendsApply = "/backends/apply"
routeNodes = "/api/nodes"
routeVRAMEstimate = "/api/models/vram-estimate"
routeBranding = "/api/branding"
routeSettings = "/api/settings"
routeUsage = "/api/usage"
routeUsageAll = "/api/usage/all"
routePIIEvents = "/api/pii/events"
routeMiddleware = "/api/middleware/status"
routeRouterDecisions = "/api/router/decisions"
routeVoiceProfiles = "/api/voice-profiles"
)
func routeJobStatus(jobID string) string {
return "/models/jobs/" + url.PathEscape(jobID)
}
func routeModelDelete(name string) string {
return "/models/delete/" + url.PathEscape(name)
}
func routeModelConfigJSON(name string) string {
return "/api/models/config-json/" + url.PathEscape(name)
}
func routeBackendUpgrade(name string) string {
return "/backends/upgrade/" + url.PathEscape(name)
}
func routeToggleModelState(name, action string) string {
return fmt.Sprintf("/models/toggle-state/%s/%s", url.PathEscape(name), url.PathEscape(action))
}
func routeToggleModelPinned(name, action string) string {
return fmt.Sprintf("/models/toggle-pinned/%s/%s", url.PathEscape(name), url.PathEscape(action))
}
func routeVoiceProfileDelete(id string) string {
return "/api/voice-profiles/" + url.PathEscape(id)
}
func routeNodeVRAMBudget(id string) string {
return "/api/nodes/" + url.PathEscape(id) + "/vram-budget"
}