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.
|
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.
|
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",
|
"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.",
|
"description": "Inspect, manage, and chat with local Ollama models, including shared persistent conversations, performance metrics, images, PDFs, URLs, and live memory telemetry.",
|
||||||
"icon": "Cpu",
|
"icon": "Cpu",
|
||||||
"version": "1.5.17",
|
"version": "1.5.18",
|
||||||
"tab": {"path": "/ollama-manager", "position": "after:models"},
|
"tab": {"path": "/ollama-manager", "position": "after:models"},
|
||||||
"entry": "dist/index.js",
|
"entry": "dist/index.js",
|
||||||
"css": "dist/style.css",
|
"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"]]
|
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]]:
|
def _fetch_family_variants(family: str) -> list[dict[str, Any]]:
|
||||||
try:
|
try:
|
||||||
html = _text_request(f"{REMOTE_OLLAMA}/library/{family}/tags", timeout=30)
|
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]]]:
|
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)})
|
families = {
|
||||||
return {family: _fetch_family_variants(family) for family in families if family}
|
_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]:
|
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)]
|
rows = [item for item in payload.get("models", []) if isinstance(item, dict) and not _is_mlx(item)]
|
||||||
catalog = {
|
catalog = {
|
||||||
"fetched_at": datetime.now(timezone.utc).isoformat(),
|
"fetched_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"source": f"{REMOTE_OLLAMA}/api/tags",
|
"source": f"{REMOTE_OLLAMA}/api/tags + {REMOTE_OLLAMA}/library",
|
||||||
"models": rows,
|
"models": rows,
|
||||||
"families": _refresh_family_variants(_local_tags()),
|
"families": _refresh_family_variants(_local_tags()),
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
name: ollama-manager
|
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.
|
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
|
auto_install_dependencies: true
|
||||||
python_dependencies:
|
python_dependencies:
|
||||||
|
|||||||
Reference in New Issue
Block a user