fix(llm): normalize Ollama model tags to lowercase
Ollama's /api/tags returns whatever casing was used at pull time (e.g. 'gemma3:12B' if the user ran 'ollama pull gemma3:12B'), but /api/chat rejects mixed-case tags with a 400. The two code paths are inconsistent, which surfaces the capitalized tag in the model dropdown and then silently kills every chat request against it. Lowercase on read (get_installed_models), on settings write (update_settings_route), and on ensure_model() input so a legacy mixed-case user setting can't trigger a spurious re-pull at startup. The dropdown and stored settings are now always in the form Ollama will actually accept. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -78,7 +78,14 @@ RAG_AUTO_SNIPPET = 4000
|
||||
|
||||
|
||||
async def get_installed_models() -> set[str]:
|
||||
"""Return set of installed Ollama model names (with and without :latest)."""
|
||||
"""Return set of installed Ollama model names (with and without :latest).
|
||||
|
||||
Tags are normalized to lowercase. Ollama stores whatever casing was used at
|
||||
pull time (``ollama pull gemma3:12B`` keeps the ``B``), but inference calls
|
||||
against the capitalized tag can 400 — the two code paths are inconsistent.
|
||||
Lowercasing on read avoids exposing that mixed-case name in the UI and
|
||||
keeps the validation check below from accepting a form Ollama will reject.
|
||||
"""
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||
resp = await client.get(f"{Config.OLLAMA_URL}/api/tags")
|
||||
@@ -86,7 +93,7 @@ async def get_installed_models() -> set[str]:
|
||||
data = resp.json()
|
||||
names: set[str] = set()
|
||||
for m in data.get("models", []):
|
||||
name = m["name"]
|
||||
name = m["name"].lower()
|
||||
names.add(name)
|
||||
if name.endswith(":latest"):
|
||||
names.add(name.removesuffix(":latest"))
|
||||
@@ -98,6 +105,9 @@ async def get_installed_models() -> set[str]:
|
||||
|
||||
async def ensure_model(model: str) -> None:
|
||||
"""Check if model exists in Ollama, pull if missing."""
|
||||
# Match the lowercase normalization in get_installed_models so a legacy
|
||||
# mixed-case setting doesn't force a spurious re-pull at startup.
|
||||
model = model.lower()
|
||||
try:
|
||||
installed = await get_installed_models()
|
||||
if model in installed or f"{model}:latest" in installed:
|
||||
|
||||
Reference in New Issue
Block a user