Enhance LLM and SwarmUI integration with improved configuration options

- Updated `env.example` and `gpu-rent.vars.example` to include new variables for LLM runtime and SwarmUI options.
- Refactored CLI commands to support interactive selection of LLM runtime and workload type (SwarmUI, LLM, or both).
- Improved access link generation to handle cases where SwarmUI is disabled, providing clearer user feedback.
- Enhanced provisioning logic to conditionally bootstrap SwarmUI based on user configuration, allowing for LLM-only setups.
- Updated documentation across multiple files to reflect changes in LLM integration, CLI usage, and configuration management.
This commit is contained in:
Leonid Pershin
2026-08-21 06:44:50 +03:00
parent f93ac5a66a
commit 7ed6a99df2
25 changed files with 455 additions and 177 deletions
+95 -21
View File
@@ -303,10 +303,10 @@ def status() -> None:
def open(
llm: bool = typer.Option(False, "--llm", help="Открыть LLM API URL вместо SwarmUI"),
) -> None:
"""Открыть браузер на SwarmUI :17801 (или --llm на Ollama/llama.cpp)."""
"""Открыть браузер на SwarmUI :17801 (или --llm / llm-only на Ollama/llama.cpp)."""
cfg = load_config(require_auth=False)
if llm:
from gpu_rent.llm_runtime import normalize_runtime
use_llm = llm or not bool(getattr(cfg, "enable_swarmui", True))
if use_llm:
from gpu_rent.access_card import resolve_llm_runtime
runtime = resolve_llm_runtime(cfg)
@@ -398,18 +398,25 @@ def up(
),
ollama: bool = typer.Option(False, "--ollama", help="То же что --llm ollama"),
llamacpp: bool = typer.Option(False, "--llamacpp", help="То же что --llm llamacpp"),
no_swarm: bool = typer.Option(
False,
"--no-swarm",
"--llm-only",
help="Только LLM на GPU, без установки SwarmUI",
),
) -> None:
"""Create/unshelve GPU, bootstrap SwarmUI, по умолчанию туннель на :17801."""
"""Create/unshelve GPU; SwarmUI и/или LLM; по умолчанию туннель."""
try:
from dataclasses import replace
from gpu_rent.llm_runtime import (
append_vars_llm_runtime,
decide_runtime,
ensure_ollama_manifest_from_example,
write_ollama_models_preset,
)
from gpu_rent.paths import vars_path
from gpu_rent.prompts import MenuItem, prompt_menu
from gpu_rent.varsfile import upsert_vars
checks = run_doctor()
code = _print_checks(checks, quiet=not verbose)
@@ -427,33 +434,93 @@ def up(
except ValueError as exc:
raise GpuRentError(str(exc)) from exc
enable_swarm = False if no_swarm else cfg.enable_swarmui
asked_model_preset = False
if not yes and runtime == "none" and not llm and not ollama and not llamacpp:
llm_flags = bool(llm or ollama or llamacpp or no_swarm)
if not yes and not llm_flags:
from gpu_rent.llm_runtime import (
llamacpp_preset_menu,
llm_runtime_menu,
ollama_preset_menu,
workload_menu,
)
from gpu_rent.prompts import prompt_menu
def _ask(msg: str, default: str = "") -> str:
return typer.prompt(msg, default=default)
default_stack = (
"llm"
if not cfg.enable_swarmui
else ("both" if runtime != "none" else "swarm")
)
try:
choice = prompt_menu(
"LLM рядом со SwarmUI",
llm_runtime_menu(),
default="none",
stack = prompt_menu(
"Что поднять на GPU",
workload_menu(),
default=default_stack,
ask=_ask,
show=log,
)
runtime = decide_runtime(
flag=choice, ollama_flag=False, llamacpp_flag=False, from_config="none"
)
except ValueError as exc:
raise GpuRentError(str(exc)) from exc
if runtime != "none" and typer.confirm("Запомнить LLM_RUNTIME в gpu-rent.vars?", default=True):
append_vars_llm_runtime(vars_path(), runtime)
if stack == "swarm":
enable_swarm = True
runtime = "none"
elif stack == "both":
enable_swarm = True
if runtime == "none":
try:
choice = prompt_menu(
"LLM runtime",
[
MenuItem("ollama", "Ollama (+ pull моделей)"),
MenuItem("llamacpp", "llama.cpp server (+ GGUF)"),
],
default="ollama",
ask=_ask,
show=log,
)
runtime = decide_runtime(
flag=choice,
ollama_flag=False,
llamacpp_flag=False,
from_config="none",
)
except ValueError as exc:
raise GpuRentError(str(exc)) from exc
else:
enable_swarm = False
if runtime == "none":
try:
choice = prompt_menu(
"LLM runtime",
[
MenuItem("ollama", "Ollama (+ pull моделей)"),
MenuItem("llamacpp", "llama.cpp server (+ GGUF)"),
],
default="llamacpp",
ask=_ask,
show=log,
)
runtime = decide_runtime(
flag=choice,
ollama_flag=False,
llamacpp_flag=False,
from_config="none",
)
except ValueError as exc:
raise GpuRentError(str(exc)) from exc
if typer.confirm("Запомнить стек в gpu-rent.vars?", default=True):
upsert_vars(
vars_path(),
{
"ENABLE_SWARMUI": "true" if enable_swarm else "false",
"LLM_RUNTIME": runtime,
},
)
if runtime == "ollama":
ensure_ollama_manifest_from_example()
try:
@@ -498,7 +565,6 @@ def up(
ollama_preset_menu,
write_llamacpp_models_preset,
)
from gpu_rent.prompts import prompt_menu
def _ask2(msg: str, default: str = "") -> str:
return typer.prompt(msg, default=default)
@@ -529,9 +595,17 @@ def up(
except ValueError as exc:
raise GpuRentError(str(exc)) from exc
cfg = replace(cfg, llm_runtime=runtime)
if runtime != "none":
ok(f"LLM runtime: {runtime}")
if not enable_swarm and runtime == "none":
raise GpuRentError(
"llm-only требует --ollama / --llamacpp / --llm … "
"(или убери --no-swarm / ENABLE_SWARMUI=true)"
)
cfg = replace(cfg, llm_runtime=runtime, enable_swarmui=enable_swarm)
if enable_swarm:
ok("стек: SwarmUI" + (f" + {runtime}" if runtime != "none" else ""))
else:
ok(f"стек: llm-only ({runtime})")
def confirm(msg: str) -> bool:
return typer.confirm(msg)