feat: add RAM bypass and load safety guard
This commit is contained in:
Vendored
+10
-3
@@ -132,6 +132,7 @@
|
||||
if (!installed) badges.push(h(Badge, { key: "available", tone: "download" }, "available"));
|
||||
if (model.popularity_rank) badges.push(h(Badge, { key: "popular", tone: "popular" }, "#" + model.popularity_rank + " popular"));
|
||||
if (model.is_moe) badges.push(h(Badge, { key: "moe", tone: "moe" }, "MoE"));
|
||||
if (model.memory_fit === false) badges.push(h(Badge, { key: "memory", tone: "danger" }, "RAM estimate exceeds host"));
|
||||
return h("article", { className: "ollama-model-card" },
|
||||
h("div", { className: "ollama-card-top" },
|
||||
h("div", { className: "ollama-model-title" }, h("h3", null, model.name), h("div", { className: "ollama-badge-row" }, badges)),
|
||||
@@ -341,7 +342,11 @@
|
||||
if (endpoint === "/models/load") {
|
||||
var resident = Array.isArray(result.resident) ? result.resident : ((result.runtime && result.runtime.model_memory) || []).map(function (item) { return item.name; });
|
||||
var missing = Array.isArray(result.not_resident) ? result.not_resident : requested.filter(function (name) { return resident.indexOf(name) < 0; });
|
||||
setNotice(missing.length ? { warning: "Ollama kept resident: " + resident.join(", ") + ". Not resident: " + missing.join(", ") + ". This is an Ollama scheduler/capacity warning, not a plugin error." } : { ok: result.message || ("Loaded and resident: " + resident.join(", ")) });
|
||||
if (result.memory_safety && result.memory_safety.triggered) {
|
||||
setNotice({ warning: result.message + " Observed RAM: " + (result.memory_safety.usage_percent == null ? "unknown" : result.memory_safety.usage_percent + "%") + "." });
|
||||
} else {
|
||||
setNotice(missing.length ? { warning: "Ollama kept resident: " + resident.join(", ") + ". Not resident: " + missing.join(", ") + ". This is an Ollama scheduler/capacity warning, not a plugin error." } : { ok: result.message || ("Loaded and resident: " + resident.join(", ")) });
|
||||
}
|
||||
} else {
|
||||
setNotice({ ok: label + ": " + requested.join(", ") });
|
||||
}
|
||||
@@ -418,6 +423,7 @@
|
||||
var catalogCapabilityState = React.useState("all"), catalogCapability = catalogCapabilityState[0], setCatalogCapability = catalogCapabilityState[1];
|
||||
var catalogSortState = React.useState("popularity"), catalogSort = catalogSortState[0], setCatalogSort = catalogSortState[1];
|
||||
var recentOnlyState = React.useState(true), recentOnly = recentOnlyState[0], setRecentOnly = recentOnlyState[1];
|
||||
var showOversizedState = React.useState(false), showOversized = showOversizedState[0], setShowOversized = showOversizedState[1];
|
||||
var busyState = React.useState(""), busy = busyState[0], setBusy = busyState[1];
|
||||
var noticeState = React.useState(null), notice = noticeState[0], setNotice = noticeState[1];
|
||||
var targetDialogState = React.useState(null), targetDialog = targetDialogState[0], setTargetDialog = targetDialogState[1];
|
||||
@@ -440,7 +446,7 @@
|
||||
fetchJSON(API + (kind === "delete" ? "/model" : "/" + kind), { method: kind === "delete" ? "DELETE" : "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: name, target: selectedTarget || "local" }) }).then(function (result) { setNotice({ ok: result.message || "Action started." }); load(); }).catch(function (err) { setNotice({ error: err.message || String(err) }); }).finally(function () { setBusy(""); });
|
||||
}
|
||||
function refreshCatalog() { setBusy("catalog"); setNotice(null); fetchJSON(API + "/catalog/refresh", { method: "POST" }).then(function (result) { setNotice({ ok: "Catalog refreshed: " + result.count + " models." }); load(); }).catch(function (err) { setNotice({ error: err.message || String(err) }); }).finally(function () { setBusy(""); }); }
|
||||
var baseModels = data ? (tab === "installed" ? data.models || [] : tab === "popular" ? data.popular || [] : tab === "catalog" ? data.catalog || [] : []) : [];
|
||||
var baseModels = data ? (tab === "installed" ? data.models || [] : tab === "popular" ? data.popular || [] : tab === "catalog" ? (showOversized ? data.catalog_all || data.catalog || [] : data.catalog || []) : []) : [];
|
||||
var models = baseModels;
|
||||
if (tab === "catalog") {
|
||||
if (catalogType === "moe") models = models.filter(function (model) { return model.is_moe; });
|
||||
@@ -478,7 +484,8 @@
|
||||
h("label", null, "Organize", h("select", { className: "ollama-catalog-select", value: catalogSort, onChange: function (event) { setCatalogSort(event.target.value); } },
|
||||
h("option", { value: "popularity" }, "Popularity"), h("option", { value: "newest" }, "Newest"), h("option", { value: "size_asc" }, "Size: smallest first"), h("option", { value: "size_desc" }, "Size: largest first"), h("option", { value: "name" }, "Name")
|
||||
)),
|
||||
h("label", { className: "ollama-catalog-checkbox", title: "Models with no published source date remain visible." }, h("input", { type: "checkbox", checked: recentOnly, onChange: function (event) { setRecentOnly(event.target.checked); } }), h("span", null, "Hide models older than 12 months"))
|
||||
h("label", { className: "ollama-catalog-checkbox", title: "Models with no published source date remain visible." }, h("input", { type: "checkbox", checked: recentOnly, onChange: function (event) { setRecentOnly(event.target.checked); } }), h("span", null, "Hide models older than 12 months")),
|
||||
h("label", { className: "ollama-catalog-checkbox ollama-catalog-memory-bypass", title: "This only bypasses the catalog display filter; loading remains protected by the 95% RAM safety guard." }, h("input", { type: "checkbox", checked: showOversized, onChange: function (event) { setShowOversized(event.target.checked); } }), h("span", null, "Show models above estimated RAM"))
|
||||
);
|
||||
var browseToolbar = tab !== "chat" && h("div", { className: "ollama-browse-row" },
|
||||
h("input", { className: "ollama-search", value: query, placeholder: "Search models, capabilities, or strengths…", onChange: function (event) { setQuery(event.target.value); } }),
|
||||
|
||||
Vendored
+1
-1
@@ -7,7 +7,7 @@
|
||||
.ollama-persistence-panel{display:grid;gap:12px;margin:16px 0;padding:16px;border:1px solid rgba(164,211,199,.16);border-radius:12px;background:rgba(10,31,28,.7)}.ollama-persistence-heading{display:flex;justify-content:space-between;gap:16px;align-items:center}.ollama-persistence-heading h3{margin:0}.ollama-persistence-heading p{margin:4px 0 0;color:#8fa9a4;font-size:11px}.ollama-conversation-list{display:flex;flex-wrap:wrap;gap:8px}.ollama-conversation-list .ollama-button{font-size:11px}.ollama-conversation-list .selected{background:#3e8073;border-color:#8dd2c1}.ollama-metrics-summary,.ollama-metrics-detail{display:flex;flex-wrap:wrap;gap:12px;font-size:11px;color:#a5bfba}.ollama-metrics-summary strong{color:#effcf8}.ollama-metrics-detail{padding-top:8px;border-top:1px solid rgba(164,211,199,.14)}
|
||||
@media(max-width:600px){.ollama-runtime-grid{grid-template-columns:1fr}.ollama-chat-model{display:block}.ollama-chat-model select{width:100%;margin-bottom:8px}.ollama-message.user{margin-left:0}.ollama-message.assistant{margin-right:0}}.ollama-connection-panel{min-width:340px;max-width:620px;margin-top:14px;padding:12px;border:1px solid rgba(164,211,199,.2);border-radius:10px;background:rgba(10,31,28,.72);box-shadow:0 8px 24px rgba(0,0,0,.12)}.ollama-connection-heading{display:flex;justify-content:space-between;gap:10px;color:#d7ebe5}.ollama-connection-heading strong{font-size:12px}.ollama-connection-heading small{display:block;margin-top:3px;color:#8fa9a4;font-size:10px}.ollama-connection-form{display:flex;gap:6px;align-items:center;margin-top:9px}.ollama-connection-role,.ollama-connection-input{border:1px solid rgba(155,205,194,.28);border-radius:6px;background:#102d29;color:#e8f2ef;padding:7px;font:inherit;font-size:11px}.ollama-connection-input{min-width:190px;flex:1}.ollama-connection-result{margin-top:7px;font-size:10px}.ollama-connection-result.ok{color:#9af1c7}.ollama-connection-result.error{color:#ffb1b1}.ollama-connection-list{display:grid;gap:5px;margin-top:8px}.ollama-connection-row{display:flex;align-items:center;gap:7px;width:100%;border:0;border-top:1px solid rgba(164,211,199,.1);padding:7px 0;background:none;color:#c5ddd7;text-align:left;cursor:pointer;font:inherit}.ollama-connection-row span:nth-child(2){display:flex;flex-direction:column;gap:2px;min-width:0}.ollama-connection-row strong{font-size:10px}.ollama-connection-row small{color:#8fa9a4;font-size:9px;overflow-wrap:anywhere}.ollama-connection-dot{width:7px;height:7px;border-radius:50%;background:#b36d6d;flex:0 0 auto}.ollama-connection-dot.online{background:#75d2b7;box-shadow:0 0 8px rgba(117,210,183,.55)}.ollama-connection-row-main{display:flex;align-items:center;gap:7px;flex:1;min-width:0;border:0;padding:0;background:none;color:inherit;text-align:left;cursor:pointer;font:inherit}.ollama-connection-remove{flex:0 0 auto;padding:5px 7px;font-size:9px}.ollama-connection-row-main>span:nth-child(2){display:flex;flex-direction:column;gap:2px;min-width:0}.ollama-connection-row-main strong{font-size:10px}.ollama-connection-row-main small{color:#8fa9a4;font-size:9px;overflow-wrap:anywhere}
|
||||
.ollama-target-modal{position:fixed;inset:0;z-index:20;display:flex;align-items:center;justify-content:center;padding:20px;background:rgba(4,15,14,.72)}.ollama-target-card{display:grid;gap:10px;max-width:560px;width:100%;padding:20px;border:1px solid rgba(141,210,193,.38);border-radius:12px;background:#102d29;box-shadow:0 14px 50px rgba(0,0,0,.35)}.ollama-target-card h3{margin:0;color:#effcf8}.ollama-target-card p{margin:0;color:#a5bfba;font-size:12px}.ollama-target-card .ollama-button{text-align:left}@media(max-width:900px){.ollama-connection-panel{min-width:0;max-width:none}.ollama-connection-form{flex-wrap:wrap}.ollama-connection-input{min-width:160px}}
|
||||
.ollama-catalog-controls{display:grid;grid-template-columns:repeat(3,minmax(130px,1fr));gap:8px;align-items:end;margin-top:0;padding:10px;border:1px solid rgba(164,211,199,.16);border-radius:10px;background:rgba(10,31,28,.55)}.ollama-catalog-controls label{display:flex;flex-direction:column;gap:5px;color:#a5bfba;font-size:10px;text-transform:uppercase;letter-spacing:.06em}.ollama-catalog-checkbox{display:flex!important;flex-direction:row!important;align-items:center;gap:8px;grid-column:1 / -1;padding:8px 4px;color:#b8ead9!important;text-transform:none!important;letter-spacing:normal!important;cursor:pointer}.ollama-catalog-checkbox input{width:15px;height:15px;margin:0;accent-color:#75d2b7}.ollama-catalog-checkbox span{font-size:11px}.ollama-catalog-select{min-width:145px;border:1px solid rgba(155,205,194,.28);border-radius:7px;background:#102d29;color:#e8f2ef;padding:8px;font:inherit;font-size:11px;text-transform:none;letter-spacing:normal}
|
||||
.ollama-catalog-controls{display:grid;grid-template-columns:repeat(3,minmax(130px,1fr));gap:8px;align-items:end;margin-top:0;padding:10px;border:1px solid rgba(164,211,199,.16);border-radius:10px;background:rgba(10,31,28,.55)}.ollama-catalog-controls label{display:flex;flex-direction:column;gap:5px;color:#a5bfba;font-size:10px;text-transform:uppercase;letter-spacing:.06em}.ollama-catalog-checkbox{display:flex!important;flex-direction:row!important;align-items:center;gap:8px;grid-column:1 / -1;padding:8px 4px;color:#b8ead9!important;text-transform:none!important;letter-spacing:normal!important;cursor:pointer}.ollama-catalog-checkbox input{width:15px;height:15px;margin:0;accent-color:#75d2b7}.ollama-catalog-checkbox span{font-size:11px}.ollama-catalog-memory-bypass{color:#ffd89a!important;background:rgba(142,90,25,.12);border-radius:7px}.ollama-catalog-select{min-width:145px;border:1px solid rgba(155,205,194,.28);border-radius:7px;background:#102d29;color:#e8f2ef;padding:8px;font:inherit;font-size:11px;text-transform:none;letter-spacing:normal}
|
||||
@media(max-width:1000px){.ollama-nav-row{display:grid;grid-template-columns:1fr}.ollama-toolbar-disk{justify-self:end}.ollama-browse-row{grid-template-columns:1fr}.ollama-catalog-controls{margin-top:0}}
|
||||
@media(max-width:760px){.ollama-tabs{grid-template-columns:repeat(2,minmax(0,1fr))}.ollama-nav-row{gap:8px}.ollama-toolbar-disk{justify-self:stretch;grid-template-columns:auto auto;min-width:0}.ollama-browse-row{gap:8px}.ollama-catalog-controls{grid-template-columns:1fr;align-items:stretch}.ollama-catalog-select{width:100%}}
|
||||
.ollama-thinking-status{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-top:14px;padding:14px 16px;border:1px solid rgba(117,210,183,.42);border-radius:10px;background:linear-gradient(90deg,rgba(46,111,96,.34),rgba(24,64,57,.5));box-shadow:0 0 20px rgba(74,190,158,.08)}.ollama-thinking-copy{flex:1;min-width:200px}.ollama-thinking-details{flex-basis:100%;padding:12px;border-top:1px solid rgba(164,211,199,.17);color:#a5bfba}.ollama-thinking-detail-grid{display:grid;grid-template-columns:repeat(5,minmax(100px,1fr));gap:8px;margin-bottom:8px}.ollama-thinking-detail-grid span{display:flex;flex-direction:column;gap:3px;padding:8px;border-radius:7px;background:rgba(71,117,108,.11);font-size:10px;color:#8fb5ac}.ollama-thinking-detail-grid strong{color:#e4f4ef;font-size:11px;overflow-wrap:anywhere}.ollama-thinking-details small{font-size:10px;color:#819b96}.ollama-thinking-status .thinking-stop{color:#ffb8b8;border-color:rgba(255,110,110,.45)}.ollama-composer.drop-active{border-color:rgba(117,210,183,.8);background:linear-gradient(135deg,rgba(33,92,79,.52),rgba(23,52,48,.62));box-shadow:0 0 24px rgba(117,210,183,.16)}.ollama-drop-hint{padding:9px;border:1px dashed rgba(117,210,183,.7);border-radius:7px;text-align:center;color:#b8ead9;font-size:11px;background:rgba(117,210,183,.08)}.ollama-thinking-spinner{display:flex;align-items:center;gap:4px;min-width:28px}.ollama-thinking-spinner span{width:7px;height:7px;border-radius:50%;background:#75d2b7;animation:ollama-thinking-pulse 1.1s ease-in-out infinite}.ollama-thinking-spinner span:nth-child(2){animation-delay:.18s}.ollama-thinking-spinner span:nth-child(3){animation-delay:.36s}.ollama-thinking-copy{display:flex;flex-direction:column;gap:3px}.ollama-thinking-copy strong{color:#effcf8;font-size:13px}.ollama-thinking-copy span{color:#b9d8d0;font-size:12px}.ollama-thinking-copy small{color:#8fb5ac;font-size:10px}@keyframes ollama-thinking-pulse{0%,80%,100%{opacity:.35;transform:scale(.8)}40%{opacity:1;transform:scale(1.2)}}
|
||||
@@ -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.22",
|
||||
"version": "1.5.23",
|
||||
"tab": {"path": "/ollama-manager", "position": "after:models"},
|
||||
"entry": "dist/index.js",
|
||||
"css": "dist/style.css",
|
||||
|
||||
+91
-11
@@ -1369,6 +1369,41 @@ def _model_placement(value: str | None) -> str:
|
||||
return placement
|
||||
|
||||
|
||||
PERMANENT_LOAD_RAM_LIMIT_PERCENT = 95.0
|
||||
|
||||
|
||||
def _memory_usage_percent() -> float | None:
|
||||
mem = _read_meminfo()
|
||||
total = int(mem.get("MemTotal") or 0)
|
||||
available = int(mem.get("MemAvailable") or mem.get("MemFree") or 0)
|
||||
if not total:
|
||||
return None
|
||||
return round(max(0.0, min(100.0, (total - available) * 100 / total)), 1)
|
||||
|
||||
|
||||
def _memory_safety_check() -> dict[str, Any]:
|
||||
usage_percent = _memory_usage_percent()
|
||||
return {
|
||||
"threshold_percent": PERMANENT_LOAD_RAM_LIMIT_PERCENT,
|
||||
"usage_percent": usage_percent,
|
||||
"triggered": usage_percent is not None and usage_percent >= PERMANENT_LOAD_RAM_LIMIT_PERCENT,
|
||||
}
|
||||
|
||||
|
||||
def _unload_model(name: str) -> dict[str, Any]:
|
||||
name = _require_installed_model(name)
|
||||
try:
|
||||
_json_request(
|
||||
LOCAL_OLLAMA + "/api/generate",
|
||||
method="POST",
|
||||
payload={"model": name, "prompt": "", "stream": False, "keep_alive": 0},
|
||||
timeout=120,
|
||||
)
|
||||
return {"name": name, "ok": True}
|
||||
except Exception as exc:
|
||||
return {"name": name, "ok": False, "error": str(exc)}
|
||||
|
||||
|
||||
def _load_model(name: str, placement: str = "gpu_ram") -> dict[str, Any]:
|
||||
name = _require_installed_model(name)
|
||||
placement = _model_placement(placement)
|
||||
@@ -1633,50 +1668,91 @@ def models_load(body: ModelsRequest) -> dict[str, Any]:
|
||||
if not names:
|
||||
raise HTTPException(400, "Select at least one model to load")
|
||||
placements = {name: _model_placement(body.placements.get(name)) for name in names}
|
||||
# Load RAM-only models first, then GPU+RAM models. This avoids asking the
|
||||
# GPU scheduler to rearrange an already GPU-resident runner unnecessarily.
|
||||
ordered_names = sorted(names, key=lambda name: 0 if placements[name] == "ram_only" else 1)
|
||||
initial_resident = {str(row.get("name") or row.get("model")) for row in _local_ps()}
|
||||
started_by_action: list[str] = []
|
||||
unloaded_by_safety: list[dict[str, Any]] = []
|
||||
results_by_name: dict[str, dict[str, Any]] = {}
|
||||
safety = _memory_safety_check()
|
||||
safety_triggered = bool(safety["triggered"])
|
||||
|
||||
for pass_index in range(2):
|
||||
if safety_triggered:
|
||||
break
|
||||
resident_now = {str(row.get("name") or row.get("model")) for row in _local_ps()}
|
||||
missing_now = [name for name in ordered_names if name not in resident_now]
|
||||
if not missing_now:
|
||||
break
|
||||
for name in missing_now:
|
||||
safety = _memory_safety_check()
|
||||
if safety["triggered"]:
|
||||
safety_triggered = True
|
||||
break
|
||||
placement = placements[name]
|
||||
stage = "Loading into GPU + RAM" if placement == "gpu_ram" else "Loading into system RAM only"
|
||||
_model_load_update(name, active=True, state="loading", stage=stage + " · pass " + str(pass_index + 1), attempt=pass_index + 1)
|
||||
try:
|
||||
result = _load_model(name, placement)
|
||||
results_by_name[name] = {"name": name, "placement": placement, "ok": True, "result": result, "attempts": pass_index + 1}
|
||||
if name not in initial_resident and name not in started_by_action:
|
||||
started_by_action.append(name)
|
||||
_model_load_update(name, state="checking", stage="Checking Ollama resident state", attempt=pass_index + 1)
|
||||
safety = _memory_safety_check()
|
||||
if safety["triggered"]:
|
||||
safety_triggered = True
|
||||
break
|
||||
except Exception as exc:
|
||||
results_by_name[name] = {"name": name, "placement": placement, "ok": False, "error": str(exc), "attempts": pass_index + 1}
|
||||
_model_load_update(name, active=False, state="failed", stage="Ollama load failed", finished_at=time.time(), error=str(exc), attempt=pass_index + 1)
|
||||
|
||||
if safety_triggered:
|
||||
for name in reversed(started_by_action):
|
||||
unloaded = _unload_model(name)
|
||||
unloaded_by_safety.append(unloaded)
|
||||
_model_load_update(name, active=False, state="safety_rollback", stage="Unloaded after 95% RAM safety stop", finished_at=time.time())
|
||||
|
||||
resident_rows = _local_ps()
|
||||
resident_names = {str(row.get("name") or row.get("model")) for row in resident_rows}
|
||||
results = [results_by_name.get(name, {"name": name, "placement": placements[name], "ok": name in resident_names, "attempts": 0}) for name in names]
|
||||
for item in results:
|
||||
item["resident"] = item["name"] in resident_names
|
||||
results = []
|
||||
for name in names:
|
||||
item = results_by_name.get(name, {"name": name, "placement": placements[name], "ok": name in resident_names, "attempts": 0})
|
||||
item["resident"] = name in resident_names
|
||||
if safety_triggered and name not in results_by_name:
|
||||
item["ok"] = False
|
||||
item["error"] = "Loading stopped by 95% RAM safety limit"
|
||||
results.append(item)
|
||||
not_resident = [name for name in names if name not in resident_names]
|
||||
for item in results:
|
||||
if item["name"] in resident_names:
|
||||
_model_load_update(item["name"], active=False, state="resident", stage="Model is resident in Ollama", finished_at=time.time())
|
||||
elif item["ok"]:
|
||||
elif item.get("ok"):
|
||||
_model_load_update(item["name"], active=False, state="evicted", stage="Ollama did not retain this model", finished_at=time.time())
|
||||
runtime = _runtime_snapshot()
|
||||
retry_count = sum(max(0, int(item.get("attempts") or 0) - 1) for item in results)
|
||||
memory_safety = {
|
||||
**safety,
|
||||
"triggered": safety_triggered,
|
||||
"unloaded_by_safety": unloaded_by_safety,
|
||||
"message": (
|
||||
"Permanent loading stopped because host RAM reached the 95% safety limit. Models started by this action were unloaded; models resident before this action were preserved."
|
||||
if safety_triggered
|
||||
else "RAM remained below the 95% permanent-load safety limit."
|
||||
),
|
||||
}
|
||||
return {
|
||||
"ok": bool(results) and not not_resident and all(item["ok"] for item in results),
|
||||
"ok": bool(results) and not not_resident and all(item["ok"] for item in results) and not safety_triggered,
|
||||
"requested": names,
|
||||
"resident": sorted(resident_names),
|
||||
"not_resident": not_resident,
|
||||
"results": results,
|
||||
"retries": retry_count,
|
||||
"runtime": runtime,
|
||||
"memory_safety": memory_safety,
|
||||
"keep_alive": "permanent",
|
||||
"message": (
|
||||
"All selected models are resident. Automatic Ollama eviction recovery completed."
|
||||
memory_safety["message"]
|
||||
if safety_triggered
|
||||
else "All selected models are resident. Automatic Ollama eviction recovery completed."
|
||||
if not not_resident and retry_count
|
||||
else "All selected models are resident."
|
||||
if not not_resident
|
||||
@@ -1876,18 +1952,21 @@ def status() -> dict[str, Any]:
|
||||
if str(variant.get("name") or variant.get("model") or "") not in catalog_names
|
||||
]
|
||||
downloadable = []
|
||||
all_downloadable = []
|
||||
seen_downloads: set[str] = set()
|
||||
for rank, row in enumerate(candidate_rows, 1):
|
||||
name = str(row.get("name") or row.get("model") or "")
|
||||
if _is_mlx(row) or not name or name in installed_names or name in seen_downloads:
|
||||
continue
|
||||
view = catalog_view(row, "catalog")
|
||||
if not _known_ram_fit(view):
|
||||
continue
|
||||
view["memory_fit"] = _known_ram_fit(view)
|
||||
view["memory_warning"] = "Estimated runtime RAM exceeds detected host RAM" if view["memory_fit"] is False else ""
|
||||
if name in catalog_names:
|
||||
view["popularity_rank"] = rank
|
||||
seen_downloads.add(name)
|
||||
downloadable.append(view)
|
||||
all_downloadable.append(view)
|
||||
if view["memory_fit"]:
|
||||
downloadable.append(dict(view))
|
||||
|
||||
popular = _popular_fit_models(
|
||||
[row for row in catalog.get("models", []) if not _is_mlx(row)],
|
||||
@@ -1936,6 +2015,7 @@ def status() -> dict[str, Any]:
|
||||
"smaller_fit_variants_substituted": True,
|
||||
},
|
||||
"catalog": downloadable,
|
||||
"catalog_all": all_downloadable,
|
||||
"catalog_filter": {
|
||||
"max_expected_ram_gib": _host_ram_gib(),
|
||||
"basis": "detected MemTotal from the running host",
|
||||
|
||||
Reference in New Issue
Block a user