Stop stacking Ollama -cpu embed tags and verify Assistent Sqlite after seed.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+1
-1
@@ -53,7 +53,7 @@ comfy:
|
||||
| `ollama` | только при `LLM_RUNTIME=ollama` |
|
||||
| `any-llm` | при `LLM_RUNTIME=ollama` |
|
||||
|
||||
Строки с несовпавшим `requires` пропускаются (лог), остальные ставятся как обычно. В `extensions.example.yaml` по умолчанию — **swarm-assistent** с `requires: ollama` (чат + доска Generate/Ref + persona/Cards, ветка `main`). После `seed-extensions` + рестарта SwarmUI подтягивается текущий `main` (с **0.10.11**: default chat = `default_chat` / старший тег, warm той же моделью). На `up` (и `gpu-rent seed-personas`) сидится папка `assistent-personas/` → overlay `/mnt/swarm_data/Assistent/personas/<id>/*.json`, плюс `_base/assistant.json` (`default_persona`, опционально `num_ctx` с GPU tier). Seed **не** удаляет overlay-личности, созданные в UI. Legacy `personas.json` / yaml-prompt больше не пишутся.
|
||||
Строки с несовпавшим `requires` пропускаются (лог), остальные ставятся как обычно. В `extensions.example.yaml` по умолчанию — **swarm-assistent** с `requires: ollama` (чат + доска Generate/Ref + persona/Cards, ветка `main`). После `seed-extensions` + рестарта SwarmUI подтягивается текущий `main` (с **0.13.1+**: `CopyLocalLockFileAssemblies` копирует `Microsoft.Data.Sqlite` + `SQLitePCLRaw*` рядом с extension DLL — иначе ListMemory/ListChats/SaveChat падают; с **0.10.11**: default chat = `default_chat` / старший тег, warm той же моделью). На `up` (и `gpu-rent seed-personas`) сидится папка `assistent-personas/` → overlay `/mnt/swarm_data/Assistent/personas/<id>/*.json`, плюс `_base/assistant.json` (`default_persona`, опционально `num_ctx` с GPU tier). Seed **не** удаляет overlay-личности, созданные в UI. Legacy `personas.json` / yaml-prompt больше не пишутся.
|
||||
|
||||
В git репозитория gpu-rent не коммитить рабочий список с лишними приватными URL сверх примера.
|
||||
|
||||
|
||||
@@ -971,10 +971,14 @@ def pull_output_cmd() -> None:
|
||||
def seed_extensions_cmd() -> None:
|
||||
"""Clone/fetch extensions.yaml, затем restart swarmui."""
|
||||
try:
|
||||
from gpu_rent.provision import verify_assistent_sqlite_bins
|
||||
|
||||
cfg, host = _live()
|
||||
|
||||
seed_extensions(cfg, host, log)
|
||||
ensure_swarmui_running(cfg, host, log, restart=True)
|
||||
# Build lands Sqlite private deps beside the extension DLL (≥0.13.1).
|
||||
verify_assistent_sqlite_bins(cfg, host, log)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
|
||||
@@ -30,9 +30,13 @@ for root in roots:
|
||||
for p in sorted(root.iterdir()):
|
||||
if "assistent" not in p.name.lower():
|
||||
continue
|
||||
dll_dir = p / "bin" / "Debug" / "net8.0"
|
||||
dll = dll_dir / "SwarmAssistentExtension.dll"
|
||||
sqlite_dll = dll_dir / "Microsoft.Data.Sqlite.dll"
|
||||
dlls = sorted(p.glob("bin/**/SwarmAssistentExtension.dll"))
|
||||
if not dlls:
|
||||
# Older layout / alternate assembly name
|
||||
dlls = sorted(p.glob("bin/**/*Assistent*.dll"))
|
||||
dll = dlls[0] if dlls else None
|
||||
dll_dir = dll.parent if dll else None
|
||||
sqlite_dll = (dll_dir / "Microsoft.Data.Sqlite.dll") if dll_dir else None
|
||||
csproj = next(p.glob("*.csproj"), None)
|
||||
tab = p / "Tabs" / "Text2Image" / "Assistent.html"
|
||||
bundle = p / "Assets" / "assistent.bundle.js"
|
||||
@@ -49,10 +53,11 @@ for root in roots:
|
||||
"path": str(p),
|
||||
"name": p.name,
|
||||
"csproj": str(csproj) if csproj else None,
|
||||
"dll": str(dll) if dll.is_file() else None,
|
||||
"dll_mtime": int(dll.stat().st_mtime) if dll.is_file() else None,
|
||||
"sqlite_dll": sqlite_dll.is_file(),
|
||||
"sqlitepcl": any(dll_dir.glob("SQLitePCLRaw*.dll")) if dll_dir.is_dir() else False,
|
||||
"dll": str(dll) if dll else None,
|
||||
"dll_mtime": int(dll.stat().st_mtime) if dll else None,
|
||||
"dll_dir": str(dll_dir) if dll_dir else None,
|
||||
"sqlite_dll": bool(sqlite_dll and sqlite_dll.is_file()),
|
||||
"sqlitepcl": any(dll_dir.glob("SQLitePCLRaw*.dll")) if dll_dir else False,
|
||||
"tab_html": tab.is_file(),
|
||||
"bundle_js": bundle.is_file(),
|
||||
"git_head": head or None,
|
||||
@@ -412,6 +417,19 @@ def collect_assistent_roles(cfg: Config, *, fs: dict[str, Any] | None = None) ->
|
||||
missing_mem = [m for m in memory if models and m not in models]
|
||||
if missing_chat:
|
||||
ok = False
|
||||
from gpu_rent.llm_runtime import is_stacked_cpu_ollama_tag
|
||||
|
||||
stacked = [
|
||||
m
|
||||
for m in list(memory) + [x for x in models if isinstance(x, str)]
|
||||
if isinstance(m, str) and is_stacked_cpu_ollama_tag(m)
|
||||
]
|
||||
if stacked:
|
||||
hints.append(
|
||||
"recursive *-cpu Ollama tags — roles.memory должен быть nomic-embed-text-cpu; "
|
||||
"ollama rm … затем re-up / provision_llm"
|
||||
)
|
||||
ok = False
|
||||
return {
|
||||
"ok": ok,
|
||||
"roles": {
|
||||
|
||||
@@ -173,6 +173,96 @@ def already_have_ollama_tag(have: set[str], wanted: str) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def ollama_model_root(name: str) -> str:
|
||||
"""Strip ``:tag`` and any trailing ``-cpu`` suffixes.
|
||||
|
||||
``nomic-embed-text-cpu-cpu:latest`` → ``nomic-embed-text``.
|
||||
"""
|
||||
base = str(name or "").split(":", 1)[0].strip()
|
||||
while base.endswith("-cpu"):
|
||||
base = base[: -len("-cpu")]
|
||||
return base
|
||||
|
||||
|
||||
def is_cpu_ollama_tag(name: str) -> bool:
|
||||
"""True if the model name (sans ``:tag``) ends with ``-cpu``."""
|
||||
return str(name or "").split(":", 1)[0].endswith("-cpu")
|
||||
|
||||
|
||||
def is_stacked_cpu_ollama_tag(name: str) -> bool:
|
||||
"""True for recursive junk like ``nomic-embed-text-cpu-cpu``."""
|
||||
base = str(name or "").split(":", 1)[0]
|
||||
if not base.endswith("-cpu"):
|
||||
return False
|
||||
return base[: -len("-cpu")].endswith("-cpu")
|
||||
|
||||
|
||||
def cpu_ollama_tag(name: str) -> str:
|
||||
"""Single CPU variant tag: ``nomic-embed-text`` / ``…-cpu-cpu`` → ``nomic-embed-text-cpu``."""
|
||||
root = ollama_model_root(name)
|
||||
return f"{root}-cpu" if root else ""
|
||||
|
||||
|
||||
def pick_ollama_from_tag(have: set[str], wanted: str) -> str | None:
|
||||
"""Pick a FROM tag for a CPU Modelfile — prefer the non-cpu library pull."""
|
||||
root = ollama_model_root(wanted)
|
||||
if not root:
|
||||
return None
|
||||
for candidate in (root, f"{root}:latest"):
|
||||
if already_have_ollama_tag(have, candidate):
|
||||
if candidate in have:
|
||||
return candidate
|
||||
if f"{root}:latest" in have:
|
||||
return f"{root}:latest"
|
||||
if root in have:
|
||||
return root
|
||||
non_cpu: list[str] = []
|
||||
single_cpu: list[str] = []
|
||||
for tag in sorted(have):
|
||||
if ollama_model_root(tag) != root:
|
||||
continue
|
||||
if is_stacked_cpu_ollama_tag(tag):
|
||||
continue
|
||||
if is_cpu_ollama_tag(tag):
|
||||
single_cpu.append(tag)
|
||||
else:
|
||||
non_cpu.append(tag)
|
||||
if non_cpu:
|
||||
return non_cpu[0]
|
||||
if single_cpu:
|
||||
return single_cpu[0]
|
||||
return None
|
||||
|
||||
|
||||
def finalize_memory_role_tags(
|
||||
memory: list[str],
|
||||
*,
|
||||
prefer_cpu: bool = True,
|
||||
) -> list[str]:
|
||||
"""One tag per embed root; collapse stacked ``-cpu`` names; optionally prefer ``*-cpu``."""
|
||||
by_root: dict[str, str] = {}
|
||||
for raw in memory:
|
||||
name = str(raw or "").strip()
|
||||
if not name:
|
||||
continue
|
||||
root = ollama_model_root(name)
|
||||
if not root:
|
||||
continue
|
||||
curated = cpu_ollama_tag(name) if is_cpu_ollama_tag(name) else name
|
||||
# Always normalize stacked cpu → single ``*-cpu``
|
||||
if is_stacked_cpu_ollama_tag(name):
|
||||
curated = cpu_ollama_tag(name)
|
||||
cur = by_root.get(root)
|
||||
if cur is None:
|
||||
by_root[root] = curated
|
||||
continue
|
||||
if prefer_cpu and is_cpu_ollama_tag(curated) and not is_cpu_ollama_tag(cur):
|
||||
by_root[root] = curated
|
||||
elif is_stacked_cpu_ollama_tag(cur) and not is_stacked_cpu_ollama_tag(curated):
|
||||
by_root[root] = curated
|
||||
return list(by_root.values())
|
||||
|
||||
|
||||
def preferred_ollama_model(path: Path) -> str | None:
|
||||
"""Manifest default chat model, else first chat tag (never memory/embed)."""
|
||||
entries = [e for e in parse_ollama_models(path) if e.use == "chat"]
|
||||
@@ -185,7 +275,10 @@ def preferred_ollama_model(path: Path) -> str | None:
|
||||
def ollama_roles_payload(entries: list[OllamaModelEntry]) -> dict[str, list[str] | str | None]:
|
||||
"""Sidecar for Assistent: chat/memory lists + preferred default_chat (manifest default: true)."""
|
||||
chat = [e.name for e in entries if e.use == "chat"]
|
||||
memory = [e.name for e in entries if e.use == "memory"]
|
||||
memory = finalize_memory_role_tags(
|
||||
[e.name for e in entries if e.use == "memory"],
|
||||
prefer_cpu=False,
|
||||
)
|
||||
preferred = next((e.name for e in entries if e.use == "chat" and e.default), None)
|
||||
if preferred is None and chat:
|
||||
preferred = chat[0]
|
||||
|
||||
+186
-30
@@ -197,7 +197,144 @@ def seed_extensions(cfg: Config, host: str, log: Log, *, update: bool = True) ->
|
||||
timeout=1800,
|
||||
log=log,
|
||||
)
|
||||
return "cloned " in out or "updated " in out
|
||||
changed = "cloned " in out or "updated " in out
|
||||
_log_assistent_source_sqlite_hint(cfg, host, log)
|
||||
return changed
|
||||
|
||||
|
||||
_ASSISTENT_SQLITE_SRC_PY = r"""
|
||||
from pathlib import Path
|
||||
roots = [Path("/mnt/swarm_data/Extensions"), Path("/opt/swarmui/src/Extensions")]
|
||||
found = []
|
||||
for root in roots:
|
||||
if not root.is_dir():
|
||||
continue
|
||||
for p in sorted(root.iterdir()):
|
||||
if "assistent" not in p.name.lower():
|
||||
continue
|
||||
csproj = next(p.glob("*.csproj"), None)
|
||||
text = csproj.read_text(encoding="utf-8", errors="replace") if csproj else ""
|
||||
found.append({
|
||||
"path": str(p),
|
||||
"name": p.name,
|
||||
"has_csproj": bool(csproj),
|
||||
"has_sqlite_pkg": "Microsoft.Data.Sqlite" in text,
|
||||
"has_copy_local": "CopyLocalLockFileAssemblies" in text,
|
||||
"has_ship_target": "ShipOnlySqlitePrivateDeps" in text,
|
||||
})
|
||||
print(__import__("json").dumps(found))
|
||||
"""
|
||||
|
||||
|
||||
_ASSISTENT_SQLITE_BIN_PY = r"""
|
||||
from pathlib import Path
|
||||
import json
|
||||
roots = [Path("/mnt/swarm_data/Extensions"), Path("/opt/swarmui/src/Extensions")]
|
||||
found = []
|
||||
for root in roots:
|
||||
if not root.is_dir():
|
||||
continue
|
||||
for p in sorted(root.iterdir()):
|
||||
if "assistent" not in p.name.lower():
|
||||
continue
|
||||
dlls = sorted(p.glob("bin/**/SwarmAssistentExtension.dll"))
|
||||
dll = dlls[0] if dlls else None
|
||||
dll_dir = dll.parent if dll else None
|
||||
sqlite = (dll_dir / "Microsoft.Data.Sqlite.dll") if dll_dir else None
|
||||
pcl = list(dll_dir.glob("SQLitePCLRaw*.dll")) if dll_dir else []
|
||||
found.append({
|
||||
"path": str(p),
|
||||
"name": p.name,
|
||||
"dll": str(dll) if dll else None,
|
||||
"sqlite_dll": bool(sqlite and sqlite.is_file()),
|
||||
"sqlitepcl": bool(pcl),
|
||||
})
|
||||
print(json.dumps(found))
|
||||
"""
|
||||
|
||||
|
||||
def _log_assistent_source_sqlite_hint(cfg: Config, host: str, log: Log) -> None:
|
||||
"""After clone: warn if Assistent csproj lacks Sqlite private-dep wiring (≥0.13.1)."""
|
||||
try:
|
||||
raw = run_ssh(
|
||||
cfg,
|
||||
host,
|
||||
"python3 - <<'PY'\n" + _ASSISTENT_SQLITE_SRC_PY + "\nPY",
|
||||
check=False,
|
||||
timeout=20,
|
||||
).strip()
|
||||
if not raw:
|
||||
return
|
||||
rows = json.loads(raw.splitlines()[-1])
|
||||
except Exception:
|
||||
return
|
||||
if not isinstance(rows, list) or not rows:
|
||||
return
|
||||
for row in rows:
|
||||
if not isinstance(row, dict):
|
||||
continue
|
||||
name = row.get("name") or "assistent"
|
||||
if not row.get("has_csproj"):
|
||||
continue
|
||||
ok = (
|
||||
row.get("has_sqlite_pkg")
|
||||
and row.get("has_copy_local")
|
||||
and row.get("has_ship_target")
|
||||
)
|
||||
if ok:
|
||||
log(
|
||||
f"Assistent {name}: csproj Sqlite private deps OK "
|
||||
"(нужен restart SwarmUI, чтобы dll+Sqlite оказались в bin/)"
|
||||
)
|
||||
else:
|
||||
log(
|
||||
f"⚠ Assistent {name}: csproj без Sqlite private deps "
|
||||
"(нужен swarm-assistent ≥0.13.1 на main — seed-extensions с update)"
|
||||
)
|
||||
|
||||
|
||||
def verify_assistent_sqlite_bins(cfg: Config, host: str, log: Log) -> bool:
|
||||
"""After SwarmUI build/restart: Microsoft.Data.Sqlite (+ SQLitePCLRaw) beside extension DLL."""
|
||||
try:
|
||||
raw = run_ssh(
|
||||
cfg,
|
||||
host,
|
||||
"python3 - <<'PY'\n" + _ASSISTENT_SQLITE_BIN_PY + "\nPY",
|
||||
check=False,
|
||||
timeout=25,
|
||||
).strip()
|
||||
if not raw:
|
||||
return False
|
||||
rows = json.loads(raw.splitlines()[-1])
|
||||
except Exception as exc:
|
||||
log(f"⚠ Assistent Sqlite bin check: {exc}")
|
||||
return False
|
||||
if not isinstance(rows, list) or not rows:
|
||||
log("⚠ Assistent: extension dir не найден — seed-extensions?")
|
||||
return False
|
||||
ok_any = False
|
||||
for row in rows:
|
||||
if not isinstance(row, dict):
|
||||
continue
|
||||
name = row.get("name") or "assistent"
|
||||
if not row.get("dll"):
|
||||
log(
|
||||
f"⚠ Assistent {name}: DLL ещё нет в bin/ — подожди compile после restart "
|
||||
"или смотри journalctl -u swarmui"
|
||||
)
|
||||
continue
|
||||
if row.get("sqlite_dll") and row.get("sqlitepcl"):
|
||||
log(f"Assistent {name}: Microsoft.Data.Sqlite + SQLitePCLRaw рядом с DLL")
|
||||
ok_any = True
|
||||
elif row.get("sqlite_dll"):
|
||||
log(f"Assistent {name}: Microsoft.Data.Sqlite есть (SQLitePCLRaw не найден)")
|
||||
ok_any = True
|
||||
else:
|
||||
log(
|
||||
f"⚠ Assistent {name}: DLL есть, но нет Microsoft.Data.Sqlite.dll — "
|
||||
"чат/memory API упадут. Нужен ≥0.13.1 + seed-extensions + restart"
|
||||
)
|
||||
return ok_any
|
||||
|
||||
|
||||
def _github_blob(cfg: Config) -> dict | None:
|
||||
@@ -1193,10 +1330,16 @@ def provision_llm(cfg: Config, host: str, log: Log) -> None:
|
||||
from gpu_rent.llm_runtime import (
|
||||
MEMORY_EMBED_MODEL,
|
||||
already_have_ollama_tag,
|
||||
cpu_ollama_tag,
|
||||
ensure_memory_model_entries,
|
||||
finalize_memory_role_tags,
|
||||
is_cpu_ollama_tag,
|
||||
is_stacked_cpu_ollama_tag,
|
||||
normalize_runtime,
|
||||
ollama_model_root,
|
||||
ollama_roles_payload,
|
||||
parse_ollama_models,
|
||||
pick_ollama_from_tag,
|
||||
preferred_ollama_model,
|
||||
)
|
||||
from gpu_rent.ssh_ops import run_script_sudo
|
||||
@@ -1283,31 +1426,30 @@ def provision_llm(cfg: Config, host: str, log: Log) -> None:
|
||||
# Sidecar roles for Assistent (chat vs memory selects)
|
||||
roles = ollama_roles_payload(entries)
|
||||
run_ssh(cfg, host, f"mkdir -p {DATA}/Assistent", check=False)
|
||||
put_text(
|
||||
cfg,
|
||||
host,
|
||||
f"{DATA}/Assistent/ollama-roles.json",
|
||||
json.dumps(roles, indent=2, ensure_ascii=False) + "\n",
|
||||
)
|
||||
log(f"Assistent ollama-roles: chat={len(roles['chat'])} memory={len(roles['memory'])}")
|
||||
# Pin memory models to CPU (num_gpu 0) so they run beside chat VL
|
||||
# Pin memory models to CPU (num_gpu 0) so they run beside chat VL.
|
||||
# Never stack ``-cpu`` onto an already-cpu tag (re-provision used to
|
||||
# create nomic-embed-text-cpu-cpu-… and point roles.memory at junk).
|
||||
mem_roots: list[str] = []
|
||||
for mem in roles["memory"] or [MEMORY_EMBED_MODEL]:
|
||||
if not already_have_ollama_tag(have, mem) and not already_have_ollama_tag(
|
||||
have, mem.split(":")[0]
|
||||
):
|
||||
root = ollama_model_root(str(mem))
|
||||
if root and root not in mem_roots:
|
||||
mem_roots.append(root)
|
||||
memory_out: list[str] = []
|
||||
for root in mem_roots:
|
||||
cpu_tag = cpu_ollama_tag(root)
|
||||
from_tag = pick_ollama_from_tag(have, root)
|
||||
if from_tag is None:
|
||||
log(f"⚠ memory model {root} нет в /api/tags — skip CPU pin")
|
||||
memory_out.append(root)
|
||||
continue
|
||||
base = mem
|
||||
# Prefer exact tag present in /api/tags
|
||||
for tag in sorted(have):
|
||||
if tag == mem or tag.startswith(mem.split(":")[0]):
|
||||
base = tag
|
||||
break
|
||||
cpu_tag = f"{base.split(':')[0]}-cpu"
|
||||
if already_have_ollama_tag(have, cpu_tag):
|
||||
if cpu_tag not in roles["memory"]:
|
||||
roles["memory"].append(cpu_tag)
|
||||
memory_out.append(cpu_tag)
|
||||
continue
|
||||
modelfile = f"FROM {base}\nPARAMETER num_gpu 0\n"
|
||||
if is_cpu_ollama_tag(from_tag):
|
||||
# Only a (single) cpu variant exists — do not create *-cpu-cpu
|
||||
memory_out.append(cpu_ollama_tag(from_tag))
|
||||
continue
|
||||
modelfile = f"FROM {from_tag}\nPARAMETER num_gpu 0\n"
|
||||
put_text(cfg, host, "/tmp/gpu-rent-embed.Modelfile", modelfile)
|
||||
try:
|
||||
run_ssh(
|
||||
@@ -1317,20 +1459,30 @@ def provision_llm(cfg: Config, host: str, log: Log) -> None:
|
||||
timeout=300,
|
||||
check=True,
|
||||
)
|
||||
roles["memory"] = [
|
||||
cpu_tag if x == mem or x == base else x for x in roles["memory"]
|
||||
]
|
||||
if cpu_tag not in roles["memory"]:
|
||||
roles["memory"].append(cpu_tag)
|
||||
have.add(cpu_tag)
|
||||
memory_out.append(cpu_tag)
|
||||
log(f"Ollama memory CPU model: {cpu_tag} (from {from_tag})")
|
||||
except Exception as exc:
|
||||
log(f"⚠ Ollama create {cpu_tag}: {exc}")
|
||||
memory_out.append(root)
|
||||
roles["memory"] = finalize_memory_role_tags(memory_out, prefer_cpu=True)
|
||||
put_text(
|
||||
cfg,
|
||||
host,
|
||||
f"{DATA}/Assistent/ollama-roles.json",
|
||||
json.dumps(roles, indent=2, ensure_ascii=False) + "\n",
|
||||
)
|
||||
log(f"Ollama memory CPU model: {cpu_tag} (from {base})")
|
||||
except Exception as exc:
|
||||
log(f"⚠ Ollama create {cpu_tag}: {exc}")
|
||||
log(
|
||||
f"Assistent ollama-roles: chat={len(roles['chat'])} "
|
||||
f"memory={roles['memory']}"
|
||||
)
|
||||
junk = sorted(t for t in have if is_stacked_cpu_ollama_tag(t))
|
||||
if junk:
|
||||
log(
|
||||
"⚠ Ollama recursive *-cpu tags (можно удалить: ollama rm …): "
|
||||
+ ", ".join(junk[:6])
|
||||
+ ("…" if len(junk) > 6 else "")
|
||||
)
|
||||
warm = preferred_ollama_model(cfg.ollama_models_manifest) or next(
|
||||
(
|
||||
n
|
||||
@@ -1484,6 +1636,10 @@ def provision_vm(
|
||||
|
||||
if swarm:
|
||||
ensure_swarmui_running(cfg, host, log, restart=restart)
|
||||
try:
|
||||
verify_assistent_sqlite_bins(cfg, host, log)
|
||||
except Exception as exc:
|
||||
log(f"Assistent Sqlite check: {exc}")
|
||||
run_ssh(
|
||||
cfg,
|
||||
host,
|
||||
|
||||
@@ -54,3 +54,56 @@ def test_write_preset_includes_memory(tmp_path: Path):
|
||||
entries = parse_ollama_models(path)
|
||||
assert preferred_ollama_model(path) is not None
|
||||
assert preferred_ollama_model(path) != MEMORY_EMBED_MODEL
|
||||
|
||||
|
||||
def test_cpu_ollama_tag_does_not_stack():
|
||||
from gpu_rent.llm_runtime import (
|
||||
cpu_ollama_tag,
|
||||
finalize_memory_role_tags,
|
||||
is_stacked_cpu_ollama_tag,
|
||||
ollama_model_root,
|
||||
pick_ollama_from_tag,
|
||||
)
|
||||
|
||||
assert ollama_model_root("nomic-embed-text-cpu-cpu-cpu") == "nomic-embed-text"
|
||||
assert cpu_ollama_tag("nomic-embed-text") == "nomic-embed-text-cpu"
|
||||
assert cpu_ollama_tag("nomic-embed-text-cpu") == "nomic-embed-text-cpu"
|
||||
assert cpu_ollama_tag("nomic-embed-text-cpu-cpu:latest") == "nomic-embed-text-cpu"
|
||||
assert is_stacked_cpu_ollama_tag("nomic-embed-text-cpu-cpu")
|
||||
assert not is_stacked_cpu_ollama_tag("nomic-embed-text-cpu")
|
||||
|
||||
have = {
|
||||
"nomic-embed-text",
|
||||
"nomic-embed-text-cpu",
|
||||
"nomic-embed-text-cpu-cpu",
|
||||
"nomic-embed-text-cpu-cpu-cpu",
|
||||
}
|
||||
assert pick_ollama_from_tag(have, "nomic-embed-text") == "nomic-embed-text"
|
||||
assert pick_ollama_from_tag(have, "nomic-embed-text-cpu") == "nomic-embed-text"
|
||||
# Re-provision must not FROM a stacked tag
|
||||
assert pick_ollama_from_tag(
|
||||
{"nomic-embed-text-cpu-cpu", "nomic-embed-text-cpu"},
|
||||
"nomic-embed-text-cpu",
|
||||
) == "nomic-embed-text-cpu"
|
||||
|
||||
assert finalize_memory_role_tags(
|
||||
["nomic-embed-text", "nomic-embed-text-cpu-cpu-cpu"],
|
||||
prefer_cpu=True,
|
||||
) == ["nomic-embed-text-cpu"]
|
||||
assert finalize_memory_role_tags(
|
||||
["nomic-embed-text-cpu-cpu", "nomic-embed-text"],
|
||||
prefer_cpu=True,
|
||||
) == ["nomic-embed-text-cpu"]
|
||||
|
||||
|
||||
def test_roles_payload_collapses_duplicate_memory_roots():
|
||||
from gpu_rent.llm_runtime import OllamaModelEntry, ollama_roles_payload
|
||||
|
||||
roles = ollama_roles_payload(
|
||||
[
|
||||
OllamaModelEntry(name="chat:7b", default=True, use="chat"),
|
||||
OllamaModelEntry(name="nomic-embed-text", use="memory"),
|
||||
OllamaModelEntry(name="nomic-embed-text-cpu-cpu", use="memory"),
|
||||
]
|
||||
)
|
||||
assert roles["memory"] == ["nomic-embed-text"]
|
||||
|
||||
Reference in New Issue
Block a user