feat: crawl full Ollama library catalog
This commit is contained in:
@@ -47,7 +47,10 @@ The Available downloads controls support:
|
||||
|
||||
Popularity and date ordering use upstream metadata only; the plugin does not invent popularity, dates, RAM requirements, or token metrics.
|
||||
|
||||
## CPU and GPU telemetry
|
||||
## Available catalog source
|
||||
|
||||
Available downloads now combine Ollama's popular API response with the public `https://ollama.com/library` index and each public family tag page. This means models that are not currently popular and are not installed locally—such as `ornith-1.5:9b` and `ornith-1.5:35b`—are discoverable. The catalog refresh found 235 public family slugs and 7,230 raw variants during validation. The existing MLX exclusion and host-RAM fit filter still apply, so very large variants such as `ornith-1.5:397b` remain hidden when they cannot fit the detected host RAM.
|
||||
|
||||
|
||||
The Live runtime panel now shows overall CPU usage, logical CPU count, load averages, overall GPU utilization, and per-GPU VRAM usage. When multiple logical CPUs are detected, it expands into a scrollable responsive per-core grid. When multiple GPUs are detected, it expands into a responsive per-GPU grid showing utilization, VRAM used/free, temperature, and power when the driver reports them. The grids use auto-fit sizing and bounded scrolling so the panel scales to larger CPU and GPU counts without overflowing the dashboard.
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"label": "Ollama Models",
|
||||
"description": "Inspect, manage, and chat with local Ollama models, including shared persistent conversations, performance metrics, images, PDFs, URLs, and live memory telemetry.",
|
||||
"icon": "Cpu",
|
||||
"version": "1.5.17",
|
||||
"version": "1.5.18",
|
||||
"tab": {"path": "/ollama-manager", "position": "after:models"},
|
||||
"entry": "dist/index.js",
|
||||
"css": "dist/style.css",
|
||||
|
||||
+36
-3
@@ -1037,6 +1037,22 @@ def _parse_variant_page(html: str, family: str) -> list[dict[str, Any]]:
|
||||
return [row for row in rows if not row["is_mlx"]]
|
||||
|
||||
|
||||
def _fetch_library_families() -> list[str]:
|
||||
"""Return all public model-family slugs from Ollama's library index."""
|
||||
try:
|
||||
html = _text_request(f"{REMOTE_OLLAMA}/library", timeout=30)
|
||||
except (HTTPError, URLError, OSError, ValueError):
|
||||
return []
|
||||
families: set[str] = set()
|
||||
for href in re.findall(r'href=["\'](/library/[^"\']+)["\']', html, re.I):
|
||||
path = unquote(href.split("?", 1)[0]).strip("/")
|
||||
parts = path.split("/")
|
||||
if len(parts) != 2 or not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]{0,190}", parts[1]):
|
||||
continue
|
||||
families.add(parts[1])
|
||||
return sorted(families)
|
||||
|
||||
|
||||
def _fetch_family_variants(family: str) -> list[dict[str, Any]]:
|
||||
try:
|
||||
html = _text_request(f"{REMOTE_OLLAMA}/library/{family}/tags", timeout=30)
|
||||
@@ -1065,8 +1081,25 @@ def _write_catalog(value: dict[str, Any]) -> None:
|
||||
|
||||
|
||||
def _refresh_family_variants(rows: list[dict[str, Any]]) -> dict[str, list[dict[str, Any]]]:
|
||||
families = sorted({_family_key(str(row.get("name") or row.get("model"))) for row in rows if not _is_mlx(row)})
|
||||
return {family: _fetch_family_variants(family) for family in families if family}
|
||||
families = {
|
||||
_family_key(str(row.get("name") or row.get("model")))
|
||||
for row in rows
|
||||
if not _is_mlx(row)
|
||||
}
|
||||
families.update(_fetch_library_families())
|
||||
families.discard("")
|
||||
# The public library currently contains hundreds of families. Fetch tag
|
||||
# pages concurrently so a daily refresh does not serialize network waits.
|
||||
result: dict[str, list[dict[str, Any]]] = {}
|
||||
with ThreadPoolExecutor(max_workers=8) as executor:
|
||||
futures = {executor.submit(_fetch_family_variants, family): family for family in sorted(families)}
|
||||
for future in as_completed(futures):
|
||||
family = futures[future]
|
||||
try:
|
||||
result[family] = future.result()
|
||||
except Exception:
|
||||
result[family] = []
|
||||
return result
|
||||
|
||||
|
||||
def refresh_catalog(force: bool = True) -> dict[str, Any]:
|
||||
@@ -1077,7 +1110,7 @@ def refresh_catalog(force: bool = True) -> dict[str, Any]:
|
||||
rows = [item for item in payload.get("models", []) if isinstance(item, dict) and not _is_mlx(item)]
|
||||
catalog = {
|
||||
"fetched_at": datetime.now(timezone.utc).isoformat(),
|
||||
"source": f"{REMOTE_OLLAMA}/api/tags",
|
||||
"source": f"{REMOTE_OLLAMA}/api/tags + {REMOTE_OLLAMA}/library",
|
||||
"models": rows,
|
||||
"families": _refresh_family_variants(_local_tags()),
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
name: ollama-manager
|
||||
version: 1.5.17
|
||||
version: 1.5.18
|
||||
description: Native dashboard manager and chat interface for local Ollama models, attachments, URLs, shared persistent conversations, performance metrics, and live runtime telemetry.
|
||||
auto_install_dependencies: true
|
||||
python_dependencies:
|
||||
|
||||
Reference in New Issue
Block a user