mirror of
https://github.com/mudler/LocalAI.git
synced 2026-07-30 09:57:57 -04:00
fix(config): only inject llama.cpp serving options on the llama.cpp path (#10822)
SetDefaults injected the llama.cpp server options cache_reuse (ApplyServingDefaults) and parallel (ApplyHardwareDefaults, re-applied per selected node by the distributed router) onto every model config regardless of backend. Every other backend ignores options it does not understand, so this was harmless until longcat-video, which strictly validates its options and fails LoadModel with "unknown model option(s): cache_reuse, parallel". Gate both injections behind a new UsesLlamaCppServingOptions allow-list (llama-cpp plus the empty/auto-detect case that resolves to llama.cpp from a GGUF file, mirroring how llamaCppDefaults is registered). This follows the existing UsesLlamaSamplerDefaults precedent for llama-only defaults. The typed NBatch field is deliberately left alone: it is a proto field every backend simply ignores, which is why batch never triggered the error. Also harden the longcat-video backend to warn-and-ignore unknown model options and request params through a testable select_known_options helper, matching the other LocalAI Python backends, so a future server-injected option cannot break loading again. 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>
This commit is contained in:
@@ -32,6 +32,7 @@ from longcat_utils import (
|
||||
require_bool,
|
||||
require_float,
|
||||
require_int,
|
||||
select_known_options,
|
||||
validate_dimensions,
|
||||
)
|
||||
|
||||
@@ -126,10 +127,17 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
|
||||
)
|
||||
|
||||
try:
|
||||
options = parse_options(request.Options)
|
||||
unknown = sorted(set(options) - LOAD_OPTIONS)
|
||||
if unknown:
|
||||
raise ValueError(f"unknown model option(s): {', '.join(unknown)}")
|
||||
options, ignored = select_known_options(
|
||||
parse_options(request.Options), LOAD_OPTIONS
|
||||
)
|
||||
if ignored:
|
||||
# The server injects llama.cpp serving defaults (cache_reuse,
|
||||
# parallel) onto every config; ignore what we don't understand
|
||||
# rather than fail to load.
|
||||
print(
|
||||
f"longcat-video ignoring unknown model option(s): {', '.join(ignored)}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
self._import_torch()
|
||||
if not self.torch.cuda.is_available():
|
||||
@@ -240,10 +248,14 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
|
||||
self.pipeline._interrupt = True
|
||||
|
||||
try:
|
||||
params = dict(request.params)
|
||||
unknown = sorted(set(params) - REQUEST_PARAMS)
|
||||
if unknown:
|
||||
raise ValueError(f"unknown request param(s): {', '.join(unknown)}")
|
||||
params, ignored_params = select_known_options(
|
||||
dict(request.params), REQUEST_PARAMS
|
||||
)
|
||||
if ignored_params:
|
||||
print(
|
||||
f"longcat-video ignoring unknown request param(s): {', '.join(ignored_params)}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
os.makedirs(os.path.dirname(request.dst) or ".", mode=0o750, exist_ok=True)
|
||||
if hasattr(context, "add_callback"):
|
||||
|
||||
@@ -65,6 +65,23 @@ def parse_options(values):
|
||||
return options
|
||||
|
||||
|
||||
def select_known_options(options, known):
|
||||
"""Split parsed options into the subset this backend understands and the
|
||||
unknown keys to ignore.
|
||||
|
||||
LocalAI injects serving defaults (e.g. the llama.cpp cache_reuse / parallel
|
||||
options) onto every model config regardless of backend. A backend should
|
||||
tolerate options it does not understand rather than refuse to load, matching
|
||||
the other LocalAI Python backends; the caller logs the ignored keys.
|
||||
|
||||
Returns (kept, ignored) where kept preserves the known entries and ignored is
|
||||
the sorted list of dropped keys.
|
||||
"""
|
||||
ignored = sorted(key for key in options if key not in known)
|
||||
kept = {key: value for key, value in options.items() if key in known}
|
||||
return kept, ignored
|
||||
|
||||
|
||||
def require_bool(value, name):
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
|
||||
@@ -21,6 +21,7 @@ from longcat_utils import ( # noqa: E402
|
||||
normalize_model_source,
|
||||
normalize_num_frames,
|
||||
parse_options,
|
||||
select_known_options,
|
||||
validate_dimensions,
|
||||
)
|
||||
|
||||
@@ -61,6 +62,26 @@ class LongCatUtilsTest(unittest.TestCase):
|
||||
self.assertEqual(options["source"], "https://example.com/model")
|
||||
self.assertEqual(options["flag"], True)
|
||||
|
||||
def test_select_known_options_keeps_known_and_reports_unknown(self):
|
||||
# The server injects llama.cpp serving defaults (cache_reuse, parallel)
|
||||
# onto every model config. longcat-video must ignore what it does not
|
||||
# understand rather than refuse to load - matching every other backend.
|
||||
options = {
|
||||
"use_distill": True,
|
||||
"max_segments": 4,
|
||||
"cache_reuse": 256,
|
||||
"parallel": 1,
|
||||
}
|
||||
kept, ignored = select_known_options(options, {"use_distill", "max_segments"})
|
||||
|
||||
self.assertEqual(kept, {"use_distill": True, "max_segments": 4})
|
||||
self.assertEqual(ignored, ["cache_reuse", "parallel"])
|
||||
|
||||
def test_select_known_options_reports_nothing_when_all_known(self):
|
||||
kept, ignored = select_known_options({"use_distill": True}, {"use_distill"})
|
||||
self.assertEqual(kept, {"use_distill": True})
|
||||
self.assertEqual(ignored, [])
|
||||
|
||||
def test_classify_model_accepts_only_supported_longcat_models(self):
|
||||
cases = {
|
||||
"meituan-longcat/LongCat-Video": MODEL_KIND_BASE,
|
||||
|
||||
Reference in New Issue
Block a user