From 29fe832929e4a78af22563fb99892efbea1c4948 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Wed, 26 Aug 2026 00:21:23 +1000 Subject: [PATCH] feat: crawl full Ollama library catalog --- README.md | 5 ++++- dashboard/manifest.json | 2 +- dashboard/plugin_api.py | 39 ++++++++++++++++++++++++++++++++++++--- plugin.yaml | 2 +- 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 74a7a48..4cc6d07 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/dashboard/manifest.json b/dashboard/manifest.json index 8c14f32..ab7d698 100644 --- a/dashboard/manifest.json +++ b/dashboard/manifest.json @@ -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", diff --git a/dashboard/plugin_api.py b/dashboard/plugin_api.py index ca73bc6..8a6f145 100644 --- a/dashboard/plugin_api.py +++ b/dashboard/plugin_api.py @@ -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()), } diff --git a/plugin.yaml b/plugin.yaml index fd0e7c5..8e9fb5f 100644 --- a/plugin.yaml +++ b/plugin.yaml @@ -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: