Руководство по сборке локального AI-стека
Часть 5. Open WebUI поверх llama.cpp
На этом шаге мы добавляем Open WebUI — веб-интерфейс с аутентификацией, историей чатов и настройкой параметров моделей. Это финальный слой: llama-server продолжает работать как обычно (только на 127.0.0.1), а Open WebUI становится «парадной дверью» для пользователей из LAN.
Зачем нужен Open WebUI
Родной UI llama.cpp (Svelte) хорош для отладки, но у него нет:
-
Управления моделями — нельзя задать per-model системные промпты, параметры сэмплинга, отображение.
-
Многопользовательского режима — один пользователь = один контекст.
Open WebUI добавляет всё это, оставаясь тонкой надстройкой над llama-server. Он ходит к нему по OpenAI-совместимому API (/v1/chat/completions), так что llama.cpp даже не знает, что его использует другой клиент.
Архитектура
LAN 0.0.0.0:8080 (Open WebUI) -> Local 127.0.0.1:10000 llama-server
Ключевые моменты:
-
llama-serverслушает только127.0.0.1:10000. Снаружи к нему не подключиться. -
Open WebUI слушает
0.0.0.0:8080, доступен из LAN, требует логин. -
Секреты (API-ключ llama + сессионный ключ WebUI) хранятся в
.secrets(chmod 600), генерируются один раз. -
Оркестратор
run_webui.shподнимает оба процесса, ждёт готовностиllama-server, запускает WebUI, и корректно останавливает оба поCtrl+C.
Секреты: .secrets и init_secrets.sh
Перед первым запуском нужно создать .secrets — файл с двумя ключами:
|
Ключ |
Для чего |
Кто использует |
|---|---|---|
|
|
Аутентификация запросов к |
Open WebUI (передаёт в заголовке |
|
|
Подпись JWT-токенов сессий Open WebUI |
Сам Open WebUI (без него все пользователи разлогиниваются при перезапуске) |
Важно про WEBUI_SECRET_KEY. Если генерировать его при каждом запуске (secrets.token_hex(32) в Python), то при каждом перезапуске WebUI все сессии сбрасываются — пользователи вынуждены логиниться заново. Поэтому ключ хранится в файле и переиспользуется. init_secrets.sh создаёт файл один раз, при повторном запуске ничего не меняет.
Формат .secrets — простые KEY=VALUE, читаются через source в bash и через open() в Python:
# Секреты проекта. Не коммитить в git. chmod 600.LLAMA_API_KEY=<48 hex-символов>WEBUI_SECRET_KEY=<64 hex-символа>
Права 600 — только владелец может читать. Это критично: ключи дают полный доступ к серверу.
Установка Open WebUI
Open WebUI ставится в тот же venv, что и всё остальное (venv/). Это упрощает wheelhouse (один список пакетов) и не создаёт второй Python-интерпретатор.
./venv/bin/pip install --upgrade pip./venv/bin/pip install torch --index-url https://download.pytorch.org/whl/cpu./venv/bin/pip install open-webui
CPU-версия torch ставится до open-webui, иначе pip притянет CUDA-вариант с nvidia-* пакетами (более 2 ГБ лишнего).
Настройка Open WebUI: start_webui.py
Скрипт читает .secrets, выставляет env-переменные и запускает open-webui serve. Ключевые настройки:
Подключение к llama-server
os.environ["OPENAI_API_BASE_URL"] = "http://127.0.0.1:10000/v1"os.environ["OPENAI_API_KEY"] = llama_api_keyos.environ["BYPASS_MODEL_ACCESS_CONTROL"] = "True"
-
OPENAI_API_BASE_URL— адресllama-server. Порт10000— фиксированный (задан вrun_webui.shчерезLLAMA_PORT), чтобы не конфликтовать с родным UIllama.cppна8080. -
OPENAI_API_KEY— ключ из.secrets. Open WebUI передаёт его в каждом запросе,llama-serverпроверяет. -
BYPASS_MODEL_ACCESS_CONTROL— все модели из роутера видны всем пользователям WebUI, без ручного «назначения прав на модель».
Оффлайн-режим
os.environ["OFFLINE_MODE"] = "true"os.environ["HF_HUB_OFFLINE"] = "1"os.environ["RAG_EMBEDDING_MODEL_AUTO_UPDATE"] = "false"os.environ["RAG_RERANKING_MODEL_AUTO_UPDATE"] = "false"os.environ["RAG_EMBEDDING_ENGINE"] = ""os.environ["RAG_EMBEDDING_MODEL"] = ""os.environ["WHISPER_MODEL_AUTO_UPDATE"] = "false"os.environ["AUDIO_STT_ENGINE"] = ""
-
OFFLINE_MODE=true— отключает проверку обновлений и автоматические загрузки с Hugging Face. -
RAG_*— отключает RAG-эмбеддинги (иначе при первом запросе WebUI попытается скачать модель эмбеддингов ~500 МБ). -
AUDIO_STT_ENGINE=""— отключает Whisper (распознавание речи), чтобы не тянуть модель.
Ollama — отключаем
os.environ["ENABLE_OLLAMA_API"] = "false"os.environ["OLLAMA_BASE_URL"] = ""
Зачем. Open WebUI по умолчанию пытается обнаружить Ollama на localhost:11434 и «подружиться» с ним. Если Ollama не установлена, в логах появляются ошибки подключения, а иногда WebUI «залипает» на попытках достучаться и не выдает список моделей из llama-cpp сервера. Отключение через ENABLE_OLLAMA_API=false убирает эту головную боль (мы уже обожглись на этом в старом проекте).
Веб-поиск — оставляем
os.environ["ENABLE_RAG_WEB_SEARCH"] = "true"os.environ["ENABLE_WEB_SEARCH"] = "true"os.environ["WEB_SEARCH_ENGINE"] = "duckduckgo"
-
DuckDuckGo — без API-ключа, работает «из коробки», если есть интернет. Модель может использовать результаты поиска для ответа (RAG-поиск в интернете). Если сети нет — просто не работает, но не ломает чат.
Прочие мелочи
os.environ["ENABLE_IMAGE_GENERATION"] = "false" # мы не подключаем sd-server к WebUIos.environ["ENABLE_COMMUNITY_FUNCTIONS"] = "false" # не грузим сторонние плагиныos.environ["USER_AGENT"] = "OpenWebUI-Offline/1.0" # убирает предупреждение LangChainos.environ["DATA_DIR"] = DATA_DIR # данные внутри проекта
Регистрация
# os.environ["ENABLE_SIGNUP"] = "false" # по умолчанию НЕ задаём
Логика по умолчанию:
-
ENABLE_SIGNUPне задаём -> регистрация открыта. -
Первый зарегистрировавшийся пользователь автоматически становится админом.
-
После создания первого админа Open WebUI автоматически закрывает регистрацию.
-
Чтобы добавить пользователя: админ заходит в Admin -> Users -> включает регистрацию на время -> создаёт аккаунт -> закрывает обратно.
Если жёстко поставить ENABLE_SIGNUP=false до первого запуска — даже админ не сможет зарегистрироваться. Поэтому оставляем пустым.
Оркестратор: run_webui.sh
Скрипт делает пять вещей:
-
Проверяет окружение —
venv,open-webui,.secrets,run_llama_mmap.sh,start_webui.py. Если чего-то нет — создаёт/устанавливает (первый запуск). -
Проверяет порты
10000и8080— если заняты, выходит с ошибкой. -
Опционально настраивает статический IP (через
nmcli), если пользователь ответит1. Полезно, чтобы адрес сервера не менялся после перезагрузки роутера. -
Запускает
llama-serverв фоне на127.0.0.1:10000черезPORT=10000 HOST=127.0.0.1 ./run_llama_mmap.sh. Ждёт/healthдо 5 минут. -
Запускает Open WebUI на
0.0.0.0:8080черезstart_webui.py. Уходит вwait.
Обработка Ctrl+C. trap cleanup EXIT INT TERM ловит сигнал и:
-
останавливает WebUI (
SIGTERM, ждёт 10 с,SIGKILL); -
останавливает
llama-server(шлётSIGTERMв bash-обёрткуrun_llama_mmap.sh, еёtrapделаетsystemctl --user stop, сервер умирает); -
при ошибке показывает хвосты логов обоих процессов.
Про модель. run_webui.sh принимает опциональный первый аргумент — имя модели. Если передать ./run_webui.sh Qwen3.5-9B-Q4_K_M, оно уйдёт в run_llama_mmap.sh -m ..., и сервер стартует в режиме одной модели. Если не передавать — run_llama_mmap.sh запустится в router mode (список моделей в дропдауне WebUI, загрузка по требованию).
Почему router mode по умолчанию: WebUI позволяет переключать модели без перезапуска сервера. Первая загрузка модели при выборе — долгая, но дальше — комфортно. Если нужна конкретная модель сразу — передайте её аргументом, загрузится только она.
Запуск и первый вход
./run_webui.sh
Вывод:
[INFO] Запуск llama-server на 127.0.0.1:10000...[INFO] llama-server запущен (PID 12345)[INFO] Жду готовности API (до 5 мин, на HDD — долго)...[INFO] llama-server готов.[INFO] Запуск Open WebUI на 0.0.0.0:8080...[INFO] Открыть: http://127.0.0.1:8080[INFO] или: http://192.168.1.100:8080
Первый вход:
-
Откройте
http://<IP>:8080в браузере. -
Нажмите «Sign up» — создайте аккаунт. Первый пользователь = админ.
-
После создания аккаунта регистрация закрывается автоматически.
-
В дропдауне моделей — список всех
.ggufизmodels/. Выберите нужную. -
Пишите в чат. История сохраняется автоматически.
Для добавления пользователя: зайдите как админ -> Admin Panel -> Users -> кнопка «Enable Signup» -> создайте аккаунт вручную или дайте человеку зарегистрироваться -> «Disable Signup».
Управление параметрами моделей в WebUI
Одно из главных преимуществ Open WebUI — per-model настройки, которых нет в родном UI llama.cpp:
-
Admin Panel -> Models — для каждой модели можно задать:
-
System Prompt — «Ты — ассистент, который отвечает кратко» и т.п.
-
Temperature, top_p, top_k, repeat_penalty — параметры сэмплинга.
-
Function calling, vision, file upload — включить/выключить.
-
Default model — какая модель выбирается при открытии нового чата.
-
Это позволяет «настроить и забыть»: например, для OxCoder-9B — temperature=0.2 (код точнее), для Gemma-4-E4B — temperature=0.9 (РП живее). В llama.cpp такие настройки пришлось бы передавать в каждом запросе.
Код init_secrets.sh
#!/usr/bin/env bash# Создаёт .secrets с LLAMA_API_KEY и WEBUI_SECRET_KEY, если файла нет.# Идемпотентен: повторный запуск ничего не меняет.set -euo pipefailSCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"SECRETS_FILE="$SCRIPT_DIR/.secrets"if [ -f "$SECRETS_FILE" ]; then echo "[*] $SECRETS_FILE уже существует, ничего не меняю." exit 0ficommand -v openssl >/dev/null || { echo "[!] нужен openssl"; exit 1; }LLAMA_API_KEY="$(openssl rand -hex 24)"WEBUI_SECRET_KEY="$(openssl rand -hex 32)"umask 077cat > "$SECRETS_FILE" <<EOF# Секреты проекта. Не коммитить в git. chmod 600.LLAMA_API_KEY=$LLAMA_API_KEYWEBUI_SECRET_KEY=$WEBUI_SECRET_KEYEOFchmod 600 "$SECRETS_FILE"echo "[*] Создан $SECRETS_FILE"echo " LLAMA_API_KEY=${LLAMA_API_KEY:0:8}... (первые 8 символов)"echo " WEBUI_SECRET_KEY=${WEBUI_SECRET_KEY:0:8}..."
Код run_webui.sh
#!/usr/bin/env bash# Запуск llama-server (порт 10000) + Open WebUI (порт 8080).# При первом запуске: создаёт venv, ставит open-webui, генерирует .secrets.## Usage: ./run_webui.sh [model_name]# model_name — опционально; если не задан, run_llama_mmap.sh спросит интерактивно.## Env:# LLAMA_PORT=10000 порт llama-server (только localhost)# WEBUI_PORT=8080 порт Open WebUI (0.0.0.0, доступ из LAN)# ASK_FIX_IP=1 спрашивать про статический IP (0 — не спрашивать)set -euo pipefailSCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"VENV_DIR="$SCRIPT_DIR/venv"SECRETS_FILE="$SCRIPT_DIR/.secrets"OFFLINE_PACKAGES_DIR="$SCRIPT_DIR/offline_packages"LLAMA_SCRIPT="$SCRIPT_DIR/run_llama_mmap.sh"WEBUI_SCRIPT="$SCRIPT_DIR/start_webui.py"INIT_SECRETS="$SCRIPT_DIR/init_secrets.sh"LLAMA_HOST="${LLAMA_HOST:-127.0.0.1}"LLAMA_PORT="${LLAMA_PORT:-10000}"WEBUI_PORT="${WEBUI_PORT:-8080}"ASK_FIX_IP="${ASK_FIX_IP:-1}"RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'info() { echo -e "${GREEN}[INFO]${NC} $*"; }warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }error() { echo -e "${RED}[ERROR]${NC} $*" >&2; exit 1; }# ==================================================================# 1. Установка / проверка окружения# ==================================================================if ! command -v python3 &>/dev/null; then error "python3 не найден. Установите: sudo apt install python3 python3-venv python3-pip"fiif [ ! -d "$VENV_DIR" ]; then info "venv не найден, создаю $VENV_DIR..." python3 -m venv "$VENV_DIR" "$VENV_DIR/bin/pip" install --quiet --upgrade pipfi# open-webui: сначала оффлайн, потом онлайнif [ ! -x "$VENV_DIR/bin/open-webui" ]; then if [ -d "$OFFLINE_PACKAGES_DIR" ] && [ -n "$(ls -A "$OFFLINE_PACKAGES_DIR" 2>/dev/null)" ]; then info "Ставлю open-webui из offline_packages..." if ! "$VENV_DIR/bin/pip" install --no-index \ --find-links="$OFFLINE_PACKAGES_DIR" open-webui; then warn "Оффлайн-установка не удалась, пробую из PyPI..." if ! "$VENV_DIR/bin/pip" show torch &>/dev/null; then "$VENV_DIR/bin/pip" install torch --index-url https://download.pytorch.org/whl/cpu fi "$VENV_DIR/bin/pip" install open-webui fi else info "Ставлю open-webui из PyPI..." "$VENV_DIR/bin/pip" install open-webui fifi[ -x "$VENV_DIR/bin/open-webui" ] || error "open-webui не установлен в venv"# секреты[ -x "$INIT_SECRETS" ] || [ -f "$INIT_SECRETS" ] || error "Нет $INIT_SECRETS"if [ ! -f "$SECRETS_FILE" ]; then info "Секреты не найдены, генерирую..." bash "$INIT_SECRETS"fi[ -f "$LLAMA_SCRIPT" ] || error "Не найден $LLAMA_SCRIPT"[ -f "$WEBUI_SCRIPT" ] || error "Не найден $WEBUI_SCRIPT"# ==================================================================# 2. Проверка занятости портов# ==================================================================for p in "$LLAMA_PORT" "$WEBUI_PORT"; do if (echo >/dev/tcp/127.0.0.1/"$p") 2>/dev/null; then error "Порт $p уже занят. Освободите или задайте LLAMA_PORT/WEBUI_PORT." fidone# ==================================================================# 3. Опциональная настройка статического IP# ==================================================================if [ "$ASK_FIX_IP" = "1" ] && command -v nmcli &>/dev/null; then read -n1 -r -p "Сделать фиксированный IP для этого ПК? [0 - нет, 1 - да]: " fix_ip echo '' if [ "$fix_ip" = "1" ]; then echo "Настройка статического IP..." iface="$(ip route | grep default | cut -d' ' -f5 | head -1)" [ -n "$iface" ] || error "Не удалось определить сетевой интерфейс" echo "Интерфейс: $iface" read -r -p "IP-адрес (например, 192.168.1.100/24): " ipaddr read -r -p "Шлюз (Enter — авто): " gateway read -r -p "DNS (Enter — авто): " dns [ -n "$gateway" ] || gateway="$(ip route | grep default | cut -d' ' -f3 | head -1)" [ -n "$dns" ] || dns="$(nmcli dev show | grep 'IP4.DNS' | tr -s ' ' | cut -d' ' -f2 | head -1)" sudo nmcli con mod "$iface" ipv4.addresses "$ipaddr" sudo nmcli con mod "$iface" ipv4.gateway "$gateway" sudo nmcli con mod "$iface" ipv4.dns "$dns" sudo nmcli con mod "$iface" ipv4.method manual sudo nmcli con up "$iface" info "Статический IP настроен. Текущий адрес: $(hostname -I)" fifi# ==================================================================# 4. Логи и trap# ==================================================================LLAMA_LOG="$(mktemp -t llama-orch.XXXXXX.log)"WEBUI_LOG="$(mktemp -t webui-orch.XXXXXX.log)"LLAMA_PID=""WEBUI_PID=""cleanup() { local ec=$? trap - EXIT INT TERM set +e echo "" info "Завершение работы..." # WebUI if [ -n "$WEBUI_PID" ] && kill -0 "$WEBUI_PID" 2>/dev/null; then info "Останавливаю Open WebUI (PID $WEBUI_PID)..." kill "$WEBUI_PID" 2>/dev/null for _ in $(seq 1 10); do kill -0 "$WEBUI_PID" 2>/dev/null || break sleep 1 done kill -9 "$WEBUI_PID" 2>/dev/null wait "$WEBUI_PID" 2>/dev/null fi # llama (bash-обёртка). Внутри её trap сделает systemctl stop. if [ -n "$LLAMA_PID" ] && kill -0 "$LLAMA_PID" 2>/dev/null; then info "Останавливаю llama-server (PID $LLAMA_PID)..." kill "$LLAMA_PID" 2>/dev/null for _ in $(seq 1 15); do kill -0 "$LLAMA_PID" 2>/dev/null || break sleep 1 done kill -9 "$LLAMA_PID" 2>/dev/null wait "$LLAMA_PID" 2>/dev/null fi if [ "$ec" -ne 0 ] && [ "$ec" -ne 130 ]; then echo ""; warn "Хвост лога llama:"; tail -n 20 "$LLAMA_LOG" 2>/dev/null echo ""; warn "Хвост лога webui:"; tail -n 20 "$WEBUI_LOG" 2>/dev/null fi rm -f "$LLAMA_LOG" "$WEBUI_LOG" info "Все сервисы остановлены." exit "$ec"}trap cleanup EXIT INT TERM# ==================================================================# 5. Запуск llama-server в фоне# ==================================================================LLAMA_ARGS=()[ $# -ge 1 ] && LLAMA_ARGS=(-m "$1")info "Запуск llama-server на $LLAMA_HOST:$LLAMA_PORT..."info "Лог: $LLAMA_LOG"PORT="$LLAMA_PORT" HOST="$LLAMA_HOST" \ "$LLAMA_SCRIPT" "${LLAMA_ARGS[@]}" >"$LLAMA_LOG" 2>&1 &LLAMA_PID=$!info "llama-server запущен (PID $LLAMA_PID)"# ==================================================================# 6. Ожидание готовности API# ==================================================================info "Жду готовности API (до 5 мин, на HDD — долго)..."READY=0for i in $(seq 1 300); do kill -0 "$LLAMA_PID" 2>/dev/null || { warn "llama-server умер. Лог:"; tail -n 40 "$LLAMA_LOG" exit 1 } if curl -sf "http://$LLAMA_HOST:$LLAMA_PORT/health" >/dev/null 2>&1; then READY=1; break fi [ $((i % 30)) -eq 0 ] && info " ... ждём ($i с)" sleep 1done[ "$READY" -eq 1 ] || { warn "llama-server не поднялся за 5 мин. Лог:"; tail -n 40 "$LLAMA_LOG"; exit 1; }info "llama-server готов."# ==================================================================# 7. Запуск Open WebUI (foreground по сути; wait ниже)# ==================================================================LAN_IP="$(hostname -I 2>/dev/null | awk '{print $1}')"info "Запуск Open WebUI на 0.0.0.0:$WEBUI_PORT..."info "Открыть: http://127.0.0.1:$WEBUI_PORT"[ -n "$LAN_IP" ] && info " или: http://$LAN_IP:$WEBUI_PORT"WEBUI_HOST=0.0.0.0 WEBUI_PORT="$WEBUI_PORT" \ "$VENV_DIR/bin/python" "$WEBUI_SCRIPT" 2>&1 | tee -a "$WEBUI_LOG" &WEBUI_PID=$!# Ждём завершения WebUI (Ctrl+C -> trap)wait "$WEBUI_PID"
Код start_webui.py
#!/usr/bin/env python3"""Запуск Open WebUI с настройками для локального llama-server."""import osimport sysimport subprocessSCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))SECRETS_FILE = os.path.join(SCRIPT_DIR, ".secrets")DATA_DIR = os.path.join(SCRIPT_DIR, "open-webui-data")# --- Чтение секретов из .secrets ---def load_secrets(path): result = {} if not os.path.isfile(path): return result with open(path) as f: for line in f: line = line.strip() if not line or line.startswith("#") or "=" not in line: continue key, _, val = line.partition("=") result[key.strip()] = val.strip() return resultsecrets = load_secrets(SECRETS_FILE)# WEBUI_SECRET_KEY — обязателен для стабильных сессийif "WEBUI_SECRET_KEY" not in secrets: print(f"Ошибка: в {SECRETS_FILE} нет WEBUI_SECRET_KEY. " f"Запустите init_secrets.sh", file=sys.stderr) sys.exit(1)os.environ["WEBUI_SECRET_KEY"] = secrets["WEBUI_SECRET_KEY"]# LLAMA_API_KEY — для доступа к llama-serverllama_api_key = secrets.get("LLAMA_API_KEY", "sk-no-key-required")os.environ["OPENAI_API_KEY"] = llama_api_key# --- Оффлайн-режим и отключение лишнего ---os.environ["OFFLINE_MODE"] = "true"os.environ["HF_HUB_OFFLINE"] = "1"os.environ["RAG_EMBEDDING_MODEL_AUTO_UPDATE"] = "false"os.environ["RAG_RERANKING_MODEL_AUTO_UPDATE"] = "false"os.environ["RAG_EMBEDDING_ENGINE"] = ""os.environ["RAG_EMBEDDING_MODEL"] = ""os.environ["WHISPER_MODEL_AUTO_UPDATE"] = "false"os.environ["AUDIO_STT_ENGINE"] = ""os.environ["ENABLE_IMAGE_GENERATION"] = "false"os.environ["ENABLE_COMMUNITY_FUNCTIONS"] = "false"os.environ["USER_AGENT"] = "OpenWebUI-Offline/1.0"# --- Ollama off (обожглись) ---os.environ["ENABLE_OLLAMA_API"] = "false"os.environ["OLLAMA_BASE_URL"] = ""# --- Веб-поиск: оставляем (DuckDuckGo без ключа) ---os.environ["ENABLE_RAG_WEB_SEARCH"] = "true"os.environ["ENABLE_WEB_SEARCH"] = "true"os.environ["WEB_SEARCH_ENGINE"] = "duckduckgo"# --- Подключение к llama-server ---os.environ["OPENAI_API_BASE_URL"] = os.environ.get( "OPENAI_API_BASE_URL", "http://127.0.0.1:10000/v1")os.environ["BYPASS_MODEL_ACCESS_CONTROL"] = "True"# --- Директория данных ---os.environ["DATA_DIR"] = DATA_DIR# --- Запуск ---venv_bin = os.path.dirname(sys.executable)open_webui_cmd = os.path.join(venv_bin, "open-webui")if not os.path.exists(open_webui_cmd): print(f"Ошибка: не найден {open_webui_cmd}. Установлен ли open-webui в venv?", file=sys.stderr) sys.exit(1)webui_port = os.environ.get("WEBUI_PORT", "8080")webui_host = os.environ.get("WEBUI_HOST", "0.0.0.0")cmd = [open_webui_cmd, "serve", "--host", webui_host, "--port", webui_port]print(f"[*] DATA_DIR: {DATA_DIR}")print(f"[*] OPENAI_BASE: {os.environ['OPENAI_API_BASE_URL']}")print(f"[*] Ollama: отключён")print(f"[*] Web search: DuckDuckGo")print(f"[*] Команда: {' '.join(cmd)}\n")try: subprocess.run(cmd, check=True)except KeyboardInterrupt: print("\n[*] Open WebUI остановлен.")except subprocess.CalledProcessError as e: print(f"[!] Ошибка запуска Open WebUI: {e}", file=sys.stderr) sys.exit(1)
Проверка после запуска
1. llama-server отвечает:
curl -sf http://127.0.0.1:10000/health && echo OK
2. Модели видны в API (и, значит, в WebUI):
curl -s http://127.0.0.1:10000/v1/models | jq '.data[].id'
Должен вывести список всех .gguf из models/.
3. WebUI отвечает:
curl -sf http://127.0.0.1:8080/ > /dev/null && echo OK
4. Память обоих процессов:
ps -o pid,comm,rss,%mem -C llama-server,python3
llama-server ~2–3 ГБ реально (RSS может быть больше из-за mmap), Python (WebUI) ~300–500 МБ.
5. Логи при ошибке:
# Если run_webui.sh упал, он покажет хвосты автоматически.# Ручная диагностика:journalctl --user -u llama-server-* --since "5 min ago" 2>/dev/null
Подводные камни
-
WEBUI_SECRET_KEYбез файла — все пользователи разлогиниваются при перезапуске. Решение:.secrets+init_secrets.sh(уже сделано). -
Ollama «стучится» по умолчанию — ошибки в логах, иногда «залипание». Решение:
ENABLE_OLLAMA_API=false(уже сделано). -
Регистрация — если случайно поставить
ENABLE_SIGNUP=falseдо первого пользователя, войти будет невозможно. Решение: не задаватьENABLE_SIGNUPвообще. -
Порт 8080 vs 8888 —
run_webui.shиспользует8080,run_sd_media.sh—8888. Если запустить оба одновременно, конфликта не будет. Ноrun_llama_mmap.shсам по себе (безrun_webui.sh) использует8080— если вы запускаете его отдельно и одновременно с WebUI, будет конфликт. Решение:run_llama_mmap.shотдельно — на8080, а с WebUI — на10000(это уже настроено вrun_webui.sh). Если ранее запускали и тестировалиrun_llama_mmap.shна родном UI, а потом запустили WebUI — может не отобразиться интерфейс (нужно очистить кэш браузера и локальные бд браузера на этой странице). -
mmapиmemory.pressure— при работе WebUI + llama под нагрузкой следите заmemory.currentчерез cgroup (как в части 2). ЕслиMEM_MAX=10Gмало для выбранной модели — уменьшитеCTX_SIZEили поднимите лимит.
ссылка на оригинал статьи https://habr.com/ru/articles/1099532/