Update extensions and documentation for LLM integration and CLI enhancements

- Added support for a new extension, `swarm-assistent`, in `extensions.example.yaml` with a requirement for `ollama`.
- Enhanced the README.md to clarify the setup process and provide a quick start guide for using extensions.
- Updated documentation in `llm.md` to reflect the opt-in nature of LLM support and provide clearer instructions for enabling it.
- Improved the `autocomplete.md` to detail the automatic setup of word lists during the initial launch.
- Revised `cli.md` to include new commands and options related to LLM runtime handling and extension management.
- Enhanced the `spike-notes.md` to guide users through the first live run with a focus on LLM integration.
This commit is contained in:
Leonid Pershin
2026-08-21 05:49:34 +03:00
parent dc1fde9e3e
commit 71f4e4c2e3
19 changed files with 713 additions and 394 deletions
+31
View File
@@ -45,12 +45,16 @@ class ModelEntry:
url: str | None
VALID_REQUIRES = frozenset({"none", "ollama", "llamacpp", "any-llm"})
@dataclass
class GitRepo:
kind: str
url: str
ref: str
directory: str | None
requires: str = "none"
def _load_yaml(path: Path) -> Any:
@@ -96,6 +100,28 @@ def parse_models(path: Path) -> list[ModelEntry]:
return entries
def normalize_requires(value: object | None) -> str:
raw = str(value or "none").strip().lower().replace("_", "-")
if raw in {"", "none", "always", "any"}:
return "none"
if raw in VALID_REQUIRES:
return raw
raise ConfigError(
f"requires={value!r}: жду none|ollama|llamacpp|any-llm"
)
def repo_matches_runtime(repo: GitRepo, llm_runtime: str) -> bool:
"""Whether this extension should be cloned for the active LLM_RUNTIME."""
req = normalize_requires(repo.requires)
runtime = (llm_runtime or "none").strip().lower()
if req == "none":
return True
if req == "any-llm":
return runtime in {"ollama", "llamacpp"}
return runtime == req
def parse_extensions(path: Path) -> list[GitRepo]:
data = _load_yaml(path)
if not data:
@@ -112,12 +138,17 @@ def parse_extensions(path: Path) -> list[GitRepo]:
for item in items:
if not isinstance(item, dict) or not item.get("url"):
raise ConfigError(f"{path}: у {kind} нужен url")
try:
requires = normalize_requires(item.get("requires"))
except ConfigError as exc:
raise ConfigError(f"{path}: {exc}") from exc
repos.append(
GitRepo(
kind=kind,
url=str(item["url"]).strip(),
ref=str(item.get("ref") or "main"),
directory=str(item["dir"]) if item.get("dir") else None,
requires=requires,
)
)
return repos
+17 -2
View File
@@ -20,6 +20,8 @@ from gpu_rent.manifests import (
parse_extensions,
parse_models,
remote_root_for,
repo_dirname,
repo_matches_runtime,
)
from gpu_rent.ssh_ops import put_text, remote_exists, run_python, run_ssh
from gpu_rent.sync_files import pull_tree, push_tree
@@ -34,9 +36,22 @@ def _pkg_text(name: str) -> str:
def seed_extensions(cfg: Config, host: str, log: Log, *, update: bool = True) -> bool:
repos = parse_extensions(cfg.extensions_manifest)
from gpu_rent.llm_runtime import normalize_runtime
runtime = normalize_runtime(cfg.llm_runtime)
all_repos = parse_extensions(cfg.extensions_manifest)
repos = [r for r in all_repos if repo_matches_runtime(r, runtime)]
skipped = [r for r in all_repos if r not in repos]
for repo in skipped:
log(
f"extensions: пропуск {repo_dirname(repo)} "
f"(requires={repo.requires}, LLM_RUNTIME={runtime})"
)
if not repos and not update:
log("extensions.yaml пуст — стоковый SwarmUI")
if all_repos:
log("extensions: все строки отфильтрованы по requires — стоковый SwarmUI")
else:
log("extensions.yaml пуст — стоковый SwarmUI")
return False
jobs = []
for repo in repos: