diff --git a/app.py b/app.py index 40db9e7..c68f485 100644 --- a/app.py +++ b/app.py @@ -829,6 +829,31 @@ def ollama_tags(s): return [] +_caps_cache = {} + + +def ollama_caps(s, model): + if model in _caps_cache: + return _caps_cache[model] + try: + r = requests.post(f"{s['ollama_url'].rstrip('/')}/api/show", json={"model": model}, timeout=10) + if r.status_code != 200: + return None + caps = set(r.json().get("capabilities") or []) + except Exception: + return None + _caps_cache[model] = caps + return caps + + +def is_vision_model(s, model): + caps = ollama_caps(s, model) + if caps is None: + base = model.split(":")[0].lower() + return any(k in base for k in ("vl", "vision", "llava", "moondream", "bakllava", "minicpm-v", "gemma3")) + return "vision" in caps + + def ollama_has(s, model): installed = ollama_tags(s) return model in installed or f"{model}:latest" in installed @@ -899,6 +924,10 @@ def ensure_models(s, engine): if m and not ollama_has(s, m): set_stage(f"downloading model {m}") ollama_pull(s, m) + if s["made_for_kids"]: + vm = s["vision_model"].strip() + if vm and not is_vision_model(s, vm): + raise RuntimeError(f"'{vm}' is a text model and cannot check images. Pick a vision model such as qwen2.5vl:7b in Settings") def ollama_call(s, prompt, temperature=0.9, model=None, images=None, num_predict=2048, as_json=True): @@ -1858,6 +1887,7 @@ def get_settings(): "models_resolved": str(resolve_sub(g, "models_dir", "models")), "temp_resolved": str(resolve_sub(g, "temp_dir", "tmp")), "installed_models": ollama_tags(g), + "installed_vision_models": [m for m in ollama_tags(g) if is_vision_model(g, m)], "suggested_text_models": SUGGESTED_TEXT_MODELS, "suggested_vision_models": SUGGESTED_VISION_MODELS, } diff --git a/static/index.html b/static/index.html index 14e1e72..b1ec9e9 100644 --- a/static/index.html +++ b/static/index.html @@ -195,7 +195,8 @@ else if(type==="days"){const on=new Set(String(s[k]||"").split(","));input=`
v===cur))opts.push([cur,cur]);input=``} else if(type.startsWith("model:")){ const kind=type.slice(6); -const installed=data.installed_models||[]; +const allInstalled=data.installed_models||[];const vis=new Set(data.installed_vision_models||[]); +const installed=kind==="vision"?allInstalled.filter(m=>vis.has(m)):allInstalled.filter(m=>!vis.has(m)); const suggested=(kind==="vision"?data.suggested_vision_models:data.suggested_text_models)||[]; const cur=String(s[k]||""); const seen=new Set();