mirror of
https://github.com/mudler/LocalAI.git
synced 2026-07-06 06:17:25 -04:00
Embedding/score/rerank all decode or pool the whole input in one physical batch, so EffectiveBatchSize sized the batch to the full context window. For a large context that makes n_ubatch huge, and the per-device CUDA compute buffer (forward-graph scratch, ~n_ubatch * n_ctx, NOT split across GPUs) balloons into multi-GiB: a large-context embedding model then aborts on load (exitCode=-1) even with plenty of free VRAM. Reproduced with qwen3-embedding-4b (context 40960 -> n_batch 40960 -> abort) and qwen3-embedding-0.6b (n_batch 8192); pinning batch:512 avoided it. This is the same root cause as issue #10485 (a large context turns the batch into multi-GiB of scratch that must fit on a SINGLE card), but the single-pass path bypassed the VRAM headroom guard the config layer already had — it returned the unbounded context as the batch with no GPU awareness. Make the single-pass batch VRAM-aware: cap it to the largest batch whose compute buffer fits the per-device VRAM headroom, clamped to [DefaultPhysicalBatch, ctx], reusing the existing computeBufferBytesPerCell and headroom-divisor math (no duplication). Unknown per-device VRAM (0) stays conservative (DefaultPhysicalBatch, not the context) so a detection gap can't OOM. The GPU is resolved through an injectable package var (config.LocalGPU, backed by sync.Once-cached xsysinfo detection) so the per-request router call stays cheap and tests inject a deterministic device. Explicit batch: still wins. An input longer than the cap can no longer be pooled in one pass — the accepted tradeoff, since a batch that OOMs the device processes nothing. Assisted-by: Claude:claude-opus-4-8 golangci-lint go-test