front: route gguf bare alias by capability to its real engine, not nvidia

pick_engine honours the front's assignment (radeon) only if the engine
can_serve the request's required capability. But _required_cap derived
that capability from the bare alias 'coe-…-q4_k_m' — no literal 'gguf' —
so required_capability returned 'transformers' (CUDA-only). radeon is
gguf-only, failed can_serve, and the request fell through to the default
engine (nvidia), even though compute_assignment had correctly placed the
model on radeon (it sees the full '…-q4_k_m.gguf' path).

Resolve the model's configured path in _load_pins (now indexed by the
.gguf-stripped stem too) and, when the name heuristic yields
'transformers' but that path is a .gguf, correct the capability to
'gguf'. whisper/ds4 precedence is unchanged. Combined with the registry
stem-matching, a bare-alias request now lands on the owning Vulkan/AMD
engine.
Co-Authored-By: 's avatarClaude Opus 4.8 <noreply@anthropic.com>
parent f204f399
...@@ -245,20 +245,41 @@ class FrontProxy: ...@@ -245,20 +245,41 @@ class FrontProxy:
if not isinstance(m, dict): if not isinstance(m, dict):
continue continue
rec = {"engine": (m.get("engine") or "").strip() or None, rec = {"engine": (m.get("engine") or "").strip() or None,
"backend": (m.get("backend") or "").strip() or None} "backend": (m.get("backend") or "").strip() or None,
"path": (m.get("path") or m.get("id") or "").strip() or None}
for field_ in (m.get("path"), m.get("id"), m.get("alias")): for field_ in (m.get("path"), m.get("id"), m.get("alias")):
if field_: if not field_:
info[str(field_).lower()] = rec continue
info[str(field_).split("/")[-1].lower()] = rec f = str(field_).lower()
base = f.split("/")[-1]
info[f] = rec
info[base] = rec
# A gguf's automatic alias is its filename without '.gguf' — index
# the stem too so a bare-alias request resolves to this record
# (and thus its pin / gguf capability), not just the '.gguf' form.
if base.endswith(".gguf"):
info[base[:-5]] = rec
info[f[:-5]] = rec
return info return info
def _required_cap(self, path: str, model: Optional[str]) -> Optional[str]: def _required_cap(self, path: str, model: Optional[str]) -> Optional[str]:
ds4 = getattr(self.config, "ds4", None) ds4 = getattr(self.config, "ds4", None)
return _router.required_capability( info = self._model_info(model)
cap = _router.required_capability(
model, path=path, model, path=path,
backend=self._model_info(model).get("backend"), backend=info.get("backend"),
ds4_model_id=getattr(ds4, "model_id", None) if ds4 else None, ds4_model_id=getattr(ds4, "model_id", None) if ds4 else None,
ds4_enabled=bool(getattr(ds4, "enabled", False)) if ds4 else False) ds4_enabled=bool(getattr(ds4, "enabled", False)) if ds4 else False)
# The name heuristic can't see that a bare alias (e.g. '…-q4_k_m', no
# literal 'gguf') backs a .gguf file, so it falls through to
# 'transformers' (CUDA-only) and the request never reaches a Vulkan/AMD
# engine. Correct it from the model's configured path. (whisper/ds4 take
# precedence above and are left untouched.)
if cap == "transformers":
mpath = (info.get("path") or "").lower()
if mpath.endswith(".gguf"):
cap = "gguf"
return cap
@staticmethod @staticmethod
def _peek_model(body: bytes, content_type: str) -> Optional[str]: def _peek_model(body: bytes, content_type: str) -> Optional[str]:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment