Compare commits

..

3 Commits

Author SHA1 Message Date
dependabot[bot]
807b29c19b chore(deps): bump torch in /backend/python/vllm
Bumps torch from 2.9.1+cpu to 2.12.1+xpu.

---
updated-dependencies:
- dependency-name: torch
  dependency-version: 2.12.1+xpu
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-07 20:22:30 +00:00
LocalAI [bot]
8565febe45 fix(vllm): pin L4T arm64 backend to vllm==0.24.0 for GB10 stability (#10725)
The nvidia-l4t-cuda-13-arm64 vLLM backend left `vllm` unpinned, so the
prebuilt image drifted onto whatever aarch64 wheel was latest at build
time (0.23.x). On GB10 / DGX Spark (Grace Blackwell, unified memory),
0.23 crashes deterministically during cold model loads with an empty
"Engine core initialization failed" set and pins GPU memory until a host
reboot.

vLLM 0.24.0 carries vllm-project/vllm#45179 ("release cached device
memory under pressure on UMA GPUs during weight loading"), which the
reporter verified fixes the crash on GB10. Pin the L4T requirements to
0.24.0 to match the already-pinned cublas13 build
(requirements-cublas13-after.txt) and keep the image deterministic.

Editing this file also re-triggers the single-arch L4T image build via
the path filter, republishing the gallery image with 0.24.0 (the
single-arch matrix builds again after #10703).

Closes #10722


Assisted-by: Claude:claude-opus-4-8 [Claude Code]

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-07-07 20:18:23 +00:00
Roman Mazurenko
a3fdfbc0d1 feat(llama-cpp): add device selection option (#10724)
Allow llama.cpp model configs to select the backend devices used for offload, matching upstream --device behavior so users can exclude a display or debug GPU.

Signed-off-by: rvmzes <rvmzes@rvmzess-MacBook-Pro.local>
Co-authored-by: rvmzes <rvmzes@rvmzess-MacBook-Pro.local>
2026-07-07 20:09:05 +00:00
7 changed files with 37 additions and 128 deletions

View File

@@ -613,6 +613,24 @@ static void params_parse(server_context& /*ctx_server*/, const backend::ModelOpt
// starts with '-'. Applied once after the loop via common_params_parse.
std::vector<std::string> extra_argv;
auto add_device_options = [&](const std::string & devices) {
const std::regex regex{ R"([,]+)" };
std::sregex_token_iterator it{ devices.begin(), devices.end(), regex, -1 };
std::vector<std::string> split_arg{ it, {} };
for (std::string device : split_arg) {
const auto start = device.find_first_not_of(" \t\n\r");
if (start == std::string::npos) {
continue;
}
const auto end = device.find_last_not_of(" \t\n\r");
device = device.substr(start, end - start + 1);
extra_argv.push_back("--device");
extra_argv.push_back(device);
}
};
// decode options. Options are in form optname:optvale, or if booleans only optname.
for (int i = 0; i < request->options_size(); i++) {
std::string opt = request->options(i);
@@ -744,6 +762,10 @@ static void params_parse(server_context& /*ctx_server*/, const backend::ModelOpt
} else if (optval_str == "false" || optval_str == "0" || optval_str == "no" || optval_str == "off" || optval_str == "disabled") {
params.no_op_offload = false;
}
} else if (!strcmp(optname, "device") || !strcmp(optname, "devices")) {
if (optval != NULL) {
add_device_options(optval_str);
}
} else if (!strcmp(optname, "split_mode") || !strcmp(optname, "sm")) {
// Accepts: none | layer | row | tensor (the latter requires a llama.cpp build
// that includes ggml-org/llama.cpp#19378, FlashAttention enabled, and KV-cache

View File

@@ -1,6 +1,6 @@
--extra-index-url https://download.pytorch.org/whl/cpu
accelerate
torch==2.9.1+cpu
torch==2.12.1+xpu
torchvision
torchaudio
transformers

View File

@@ -1,4 +1,12 @@
# vLLM 0.20+ ships an aarch64 manylinux wheel on PyPI whose Requires-Dist pins
# torch==2.11.0 / torchvision==0.26.0 / torchaudio==2.11.0, locking an ABI-
# consistent set with the cu130 torch wheel installed above.
vllm
#
# Pinned to match the cublas13 build (requirements-cublas13-after.txt) and to
# stay deterministic on GB10 / DGX Spark: 0.24.0 carries vllm-project/vllm#45179
# ("release cached device memory under pressure on UMA GPUs during weight
# loading"), without which cold model loads on the Grace Blackwell unified-
# memory architecture crash deterministically with an empty "Engine core init
# failed" set (mudler/LocalAI#10722). Leaving this unpinned let the L4T image
# drift onto whatever wheel was latest at build time.
vllm==0.24.0

View File

@@ -23,27 +23,6 @@ import (
"github.com/mudler/LocalAI/pkg/httpclient"
)
// oidcSupportedSigningAlgs is the set of ID-token signature algorithms the OIDC
// verifier accepts. go-oidc defaults to RS256 only, which rejects identity
// providers that sign tokens with EC keys (ES*) or other algorithms — e.g.
// Authentik configured with an EC signing key fails the callback with
// "unexpected signature algorithm ... expected [RS256]" (#10677). All entries
// are asymmetric algorithms verified against the provider's published JWKS;
// HS256 is intentionally excluded (it is symmetric and would validate against
// the client secret, a different and security-sensitive trust model).
var oidcSupportedSigningAlgs = []string{
oidc.RS256, oidc.RS384, oidc.RS512,
oidc.ES256, oidc.ES384, oidc.ES512,
oidc.PS256, oidc.PS384, oidc.PS512,
oidc.EdDSA,
}
// OIDCSupportedSigningAlgs returns a copy of the ID-token signature algorithms
// the OIDC verifier accepts. Exposed for tests.
func OIDCSupportedSigningAlgs() []string {
return append([]string(nil), oidcSupportedSigningAlgs...)
}
// providerEntry holds the OAuth2/OIDC config for a single provider.
type providerEntry struct {
oauth2Config oauth2.Config
@@ -106,10 +85,7 @@ func NewOAuthManager(baseURL string, params OAuthParams) (*OAuthManager, error)
return nil, fmt.Errorf("OIDC discovery failed for %s: %w", params.OIDCIssuer, err)
}
verifier := provider.Verifier(&oidc.Config{
ClientID: params.OIDCClientID,
SupportedSigningAlgs: oidcSupportedSigningAlgs,
})
verifier := provider.Verifier(&oidc.Config{ClientID: params.OIDCClientID})
m.providers[ProviderOIDC] = &providerEntry{
name: ProviderOIDC,

View File

@@ -1,101 +0,0 @@
//go:build auth
package auth_test
import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"time"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/mudler/LocalAI/core/http/auth"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
const (
testOIDCIssuer = "https://issuer.example.test"
testOIDCClientID = "localai-test-client"
)
// signES256IDToken builds a minimal ES256-signed JWT in JOSE compact form. The
// signature is the raw R||S encoding (32 bytes each for P-256) that OIDC
// verifiers expect, not ASN.1/DER.
func signES256IDToken(key *ecdsa.PrivateKey, claims map[string]any) string {
b64 := func(b []byte) string { return base64.RawURLEncoding.EncodeToString(b) }
header := b64([]byte(`{"alg":"ES256","typ":"JWT"}`))
payloadJSON, err := json.Marshal(claims)
Expect(err).NotTo(HaveOccurred())
signingInput := header + "." + b64(payloadJSON)
digest := sha256.Sum256([]byte(signingInput))
r, s, err := ecdsa.Sign(rand.Reader, key, digest[:])
Expect(err).NotTo(HaveOccurred())
sig := make([]byte, 64)
r.FillBytes(sig[:32])
s.FillBytes(sig[32:])
return signingInput + "." + b64(sig)
}
var _ = Describe("OIDC ID-token signing algorithms", func() {
var (
ctx context.Context
key *ecdsa.PrivateKey
idToken string
keySet *oidc.StaticKeySet
)
BeforeEach(func() {
ctx = context.Background()
var err error
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
Expect(err).NotTo(HaveOccurred())
idToken = signES256IDToken(key, map[string]any{
"iss": testOIDCIssuer,
"sub": "user-1",
"aud": testOIDCClientID,
"email": "user@example.test",
"iat": time.Now().Unix(),
"exp": time.Now().Add(time.Hour).Unix(),
})
keySet = &oidc.StaticKeySet{PublicKeys: []crypto.PublicKey{key.Public()}}
})
It("accepts an EC (ES256) signed ID token with the configured algorithms", func() {
verifier := oidc.NewVerifier(testOIDCIssuer, keySet, &oidc.Config{
ClientID: testOIDCClientID,
SupportedSigningAlgs: auth.OIDCSupportedSigningAlgs(),
})
tok, err := verifier.Verify(ctx, idToken)
Expect(err).NotTo(HaveOccurred())
Expect(tok.Subject).To(Equal("user-1"))
})
It("rejects the same token under go-oidc's RS256-only default (the pre-fix behavior)", func() {
// This reproduces #10677: without a broadened SupportedSigningAlgs the
// verifier only accepts RS256 and rejects EC-signed tokens.
verifier := oidc.NewVerifier(testOIDCIssuer, keySet, &oidc.Config{
ClientID: testOIDCClientID,
})
_, err := verifier.Verify(ctx, idToken)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("ES256"))
})
It("advertises the common asymmetric algorithms and excludes the symmetric HS256", func() {
algs := auth.OIDCSupportedSigningAlgs()
Expect(algs).To(ContainElements(
oidc.RS256, oidc.RS384, oidc.RS512,
oidc.ES256, oidc.ES384, oidc.ES512,
oidc.PS256, oidc.EdDSA,
))
Expect(algs).NotTo(ContainElement("HS256"))
})
})

View File

@@ -493,6 +493,7 @@ These llama.cpp options are passed through the `options:` array.
| `threads_batch` / `n_threads_batch` | int | same as `threads` | Threads used during prompt processing. `<= 0` means `hardware_concurrency()`. |
| `direct_io` / `use_direct_io` | bool | `false` | Open the model with `O_DIRECT` (faster cold loads on NVMe; ignored if not supported). |
| `verbosity` | int | `3` | llama.cpp internal log verbosity threshold. Higher = more verbose. |
| `device` / `devices` | string | all devices | Select the llama.cpp backend devices to use. Repeat the option or pass a comma-separated list; unlisted devices are excluded. Use the names reported by `llama-server --list-devices` / `--list-devices`. |
| `override_tensor` / `tensor_buft_overrides` | string | "" | Per-tensor buffer-type overrides for the main model. Format: `<tensor regex>=<buffer type>,<tensor regex>=<buffer type>,...`. Mirrors the existing `draft_override_tensor` syntax for the draft model. |
| `cpu_moe` | bool | false | Keep all MoE expert weights of the main model on CPU (upstream `--cpu-moe`). Frees VRAM on large MoE models (DeepSeek, Qwen3 `*-A3B`). |
| `n_cpu_moe` | int | 0 | Keep MoE expert weights of the first N main-model layers on CPU (upstream `--n-cpu-moe`). |
@@ -514,6 +515,7 @@ options:
- "--cpu-moe" # boolean flag
- "--n-cpu-moe:4" # flag with a value
- "--override-tensor:exps=CPU"
- "devices:CUDA1,CUDA2,CUDA3" # skip CUDA0, e.g. a display GPU
```
Notes:

View File

@@ -512,6 +512,7 @@ The `llama.cpp` backend supports additional configuration options that can be sp
| `check_tensors` | boolean | Validate tensor data for invalid values during model loading. Default: `false`. | `check_tensors:true` |
| `warmup` | boolean | Enable warmup run after model loading. Default: `true`. | `warmup:false` |
| `no_op_offload` | boolean | Disable offloading host tensor operations to device. Default: `false`. | `no_op_offload:true` |
| `device` or `devices` | string | Select the llama.cpp backend devices to use. Repeat the option or pass a comma-separated list; unlisted devices are excluded. Use the names reported by `llama-server --list-devices` / `--list-devices`. | `devices:CUDA1,CUDA2,CUDA3` |
| `kv_unified` or `unified_kv` | boolean | Use a single unified KV buffer shared across all sequences. Default: `true` (LocalAI override; upstream defaults to `false` but auto-enables it when slot count is auto). **Required for `cache_idle_slots` to work**: without it the server force-disables idle-slot saving at init, and the prompt cache is never written across requests. | `kv_unified:false` |
| `cache_idle_slots` or `idle_slots_cache` | boolean | On a new task, save the previous slot's KV state into the prompt cache (and clear the slot) so a later request with the same prefix can warm-load it. Default: `true`. Auto-disabled by the server if `kv_unified=false` or `cache_ram=0`. | `cache_idle_slots:false` |
| `n_ctx_checkpoints` or `ctx_checkpoints` | integer | Maximum number of context checkpoints per slot (used for partial-prefix recovery, e.g. SWA). Default: `32`. | `ctx_checkpoints:16` |
@@ -530,6 +531,7 @@ options:
- context_shift:true
- cache_ram:4096
- parallel:2
- devices:CUDA1,CUDA2,CUDA3
- fit_params:true
- fit_target:1024
- slot_prompt_similarity:0.5