feat(settings): model management UI — pull, delete, VRAM status
Backend: - Enrich GET /api/chat/models to also hit /api/ps and return loaded:bool and modified_at alongside name/size, using parallel gather Frontend (Settings → General): - Model list: each row shows name (monospace), size (GB/MB), 'in VRAM' badge if currently loaded, 'default' badge if it's the configured default - Delete button per row; disabled while deletion in progress - Pull form: text input (Enter submits) + Pull button - Suggestion chips for qwen3:7b/14b/4b, llama3.1:8b, nomic-embed-text; disabled if already installed - Progress display during pull: status text + determinate bar when Ollama reports total/completed, indeterminate animation otherwise - Refresh button reloads the list; list auto-refreshes after pull/delete - Link to ollama.com/library for model discovery Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -405,14 +405,29 @@ async def chat_status_route():
|
||||
async def list_models_route():
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||
resp = await client.get(f"{Config.OLLAMA_URL}/api/tags")
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
models = [
|
||||
{"name": m["name"], "size": m.get("size", 0)}
|
||||
for m in data.get("models", [])
|
||||
]
|
||||
return jsonify({"models": models})
|
||||
tags_task = asyncio.create_task(client.get(f"{Config.OLLAMA_URL}/api/tags"))
|
||||
ps_task = asyncio.create_task(client.get(f"{Config.OLLAMA_URL}/api/ps"))
|
||||
tags_resp, ps_resp = await asyncio.gather(tags_task, ps_task, return_exceptions=True)
|
||||
|
||||
loaded_names: set[str] = set()
|
||||
if not isinstance(ps_resp, Exception):
|
||||
try:
|
||||
ps_resp.raise_for_status()
|
||||
loaded_names = {m["name"] for m in ps_resp.json().get("models", [])}
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
models = []
|
||||
if not isinstance(tags_resp, Exception):
|
||||
tags_resp.raise_for_status()
|
||||
for m in tags_resp.json().get("models", []):
|
||||
models.append({
|
||||
"name": m["name"],
|
||||
"size": m.get("size", 0),
|
||||
"modified_at": m.get("modified_at", ""),
|
||||
"loaded": m["name"] in loaded_names,
|
||||
})
|
||||
return jsonify({"models": models})
|
||||
except Exception as e:
|
||||
logger.warning("Failed to list Ollama models: %s", e)
|
||||
return jsonify({"models": [], "error": str(e)}), 200
|
||||
|
||||
Reference in New Issue
Block a user