Support Ollama use: chat|memory and parallel embed beside VL.

Pull nomic-embed-text for Assistent memory, write ollama-roles.json, CPU Modelfile, and raise MAX_LOADED_MODELS/NUM_PARALLEL to 2.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-21 22:47:03 +03:00
co-authored by Cursor
parent 44f46d8190
commit 4081890b4c
8 changed files with 188 additions and 18 deletions
+47 -5
View File
@@ -18,9 +18,12 @@ from gpu_rent.paths import (
VALID_RUNTIMES = frozenset({"none", "ollama"})
MEMORY_EMBED_MODEL = "nomic-embed-text"
OLLAMA_PRESETS: dict[str, list[str]] = {
# Requirement: uncensored (abliterated) + solid Russian. Qwen2.5 family.
# Vision tags preferred for SwarmUI prompt help with images.
# Memory embed (nomic) is appended separately with use: memory.
"recommended": ["huihui_ai/qwen2.5-vl-abliterated:7b"], # ~6GB
"light": ["huihui_ai/qwen2.5-vl-abliterated:3b"], # ~3GB
"text": ["huihui_ai/qwen2.5-abliterate:7b"], # ~5GB, no vision
@@ -79,6 +82,14 @@ def ollama_preset_menu(*, include_keep: bool = False) -> list:
class OllamaModelEntry:
name: str
default: bool = False
use: str = "chat" # chat | memory
def _normalize_use(raw: object) -> str:
s = str(raw or "chat").strip().lower()
if s in {"memory", "embed", "embedding"}:
return "memory"
return "chat"
def normalize_runtime(value: str | None) -> str:
@@ -119,17 +130,37 @@ def parse_ollama_models(path: Path) -> list[OllamaModelEntry]:
if isinstance(item, str):
name = item.strip()
if name:
out.append(OllamaModelEntry(name=name))
out.append(OllamaModelEntry(name=name, use="chat"))
continue
if not isinstance(item, dict):
continue
name = str(item.get("name") or "").strip()
if not name:
continue
out.append(OllamaModelEntry(name=name, default=bool(item.get("default"))))
use = _normalize_use(item.get("use") or item.get("role"))
out.append(
OllamaModelEntry(
name=name,
default=bool(item.get("default")) and use == "chat",
use=use,
)
)
return out
def ensure_memory_model_entries(entries: list[OllamaModelEntry]) -> list[OllamaModelEntry]:
"""Append default memory embed if the manifest has chat models but no memory."""
if not entries:
return entries
if any(e.use == "memory" for e in entries):
return entries
if all(e.use == "memory" for e in entries):
return entries
return list(entries) + [
OllamaModelEntry(name=MEMORY_EMBED_MODEL, default=False, use="memory")
]
def already_have_ollama_tag(have: set[str], wanted: str) -> bool:
"""Exact tag match only — qwen2.5:3b must not satisfy qwen2.5:7b."""
if wanted in have:
@@ -142,14 +173,20 @@ def already_have_ollama_tag(have: set[str], wanted: str) -> bool:
def preferred_ollama_model(path: Path) -> str | None:
"""Manifest default, else first tag."""
entries = parse_ollama_models(path)
"""Manifest default chat model, else first chat tag (never memory/embed)."""
entries = [e for e in parse_ollama_models(path) if e.use == "chat"]
for entry in entries:
if entry.default:
return entry.name
return entries[0].name if entries else None
def ollama_roles_payload(entries: list[OllamaModelEntry]) -> dict[str, list[str]]:
chat = [e.name for e in entries if e.use == "chat"]
memory = [e.name for e in entries if e.use == "memory"]
return {"chat": chat, "memory": memory}
def _ollama_ps_names(payload: object) -> set[str]:
names: set[str] = set()
if not isinstance(payload, dict):
@@ -233,7 +270,9 @@ def write_ollama_models_preset(path: Path, preset: str) -> None:
names = OLLAMA_PRESETS[key]
lines = [
"# Локальный манифест Ollama (не коммить). Пример: ollama-models.example.yaml",
"# name = точный тег для `ollama pull`. Пустой models: [] — без pull.",
"# name = точный тег для `ollama pull`.",
"# use: chat — селект Assistent; use: memory — модель памяти (⚙).",
"# Пустой models: [] — без pull.",
"models:",
]
if not names:
@@ -241,8 +280,11 @@ def write_ollama_models_preset(path: Path, preset: str) -> None:
else:
for i, name in enumerate(names):
lines.append(f" - name: {name}")
lines.append(" use: chat")
if i == 0:
lines.append(" default: true")
lines.append(f" - name: {MEMORY_EMBED_MODEL}")
lines.append(" use: memory")
path.write_text("\n".join(lines) + "\n", encoding="utf-8")