@@ -0,0 +1,72 @@
|
||||
WESP: локальный LLM (llama-server + GGUF Qwen2.5-1.5B-Instruct)
|
||||
===============================================================
|
||||
|
||||
Запускайте скрипты из КОРНЯ репозитория (где лежат папки app/, data/, install/).
|
||||
|
||||
1) Каталоги (логи, кэш, models):
|
||||
Linux/macOS: sh install/assistant/create_layout.sh
|
||||
Windows: powershell -ExecutionPolicy Bypass -File install\assistant\create_layout.ps1
|
||||
|
||||
Если вы уже в папке install\, путь к скрипту другой:
|
||||
powershell -ExecutionPolicy Bypass -File assistant\download_model.ps1
|
||||
|
||||
По умолчанию корень: <репозиторий>/data/assistant
|
||||
Или: set WESP_ASSISTANT_DIR=...
|
||||
|
||||
2) Скачать веса (~1.3 GB для q5_k_m):
|
||||
pip install -U huggingface_hub
|
||||
sh install/assistant/download_model.sh
|
||||
или download_model.ps1
|
||||
|
||||
3) Запуск llama-server
|
||||
Linux/macOS: sh install/assistant/run_llama_server.sh
|
||||
Windows (авто, ~15 MiB с GitHub):
|
||||
powershell -ExecutionPolicy Bypass -File install\assistant\download_llama_server.ps1
|
||||
Кладёт exe и все .dll из архива в <репо>\tools\ (run_llama_server.ps1 найдёт сам).
|
||||
Если llama-server сразу падает с кодом -1073741515 — не хватало DLL рядом с exe:
|
||||
powershell -ExecutionPolicy Bypass -File install\assistant\sync_llama_tools.ps1
|
||||
«Всё сразу» (отдельное окно llama + WESP с LLM):
|
||||
powershell -ExecutionPolicy Bypass -File .\start_chat.ps1
|
||||
Вручную: https://github.com/ggml-org/llama.cpp/releases — архив llama-*-bin-win-cpu-x64.zip,
|
||||
из него llama-server.exe в tools\ или в PATH, либо:
|
||||
$env:LLAMA_SERVER = "C:\полный\путь\llama-server.exe"
|
||||
Запуск из папки install:
|
||||
.\assistant\run_llama_server.ps1
|
||||
Из корня репозитория:
|
||||
powershell -ExecutionPolicy Bypass -File install\assistant\run_llama_server.ps1
|
||||
(Без .\ в начале PowerShell пытается загрузить «модуль» install — ошибка.)
|
||||
|
||||
4) Переменные для WESP (пример; API LLM в админке по умолчанию включён):
|
||||
set WESP_LLM_BASE_URL=http://127.0.0.1:8080
|
||||
set WESP_LLM_MODEL=qwen2.5-1.5b-instruct-q5_k_m.gguf
|
||||
Выключить LLM: set WESP_ADMIN_LLM_ENABLED=0 или галка в админке → data/wesp_security_settings.json (admin_llm_enabled).
|
||||
|
||||
Автозапуск llama-server вместе с python run.py / gunicorn (дочерний процесс, при выходе WESP завершается):
|
||||
по умолчанию ВКЛ; выключить: set WESP_LLM_AUTOSTART=0 или галка в админке (llm_autostart).
|
||||
Нужны: LLM включён в админке, файл .gguf в data/assistant/models/, llama-server в tools/ или LLAMA_SERVER=...
|
||||
|
||||
Порт в run_* должен совпадать с WESP_LLM_BASE_URL. Имя модели в WESP_LLM_MODEL — как в API (часто имя файла .gguf).
|
||||
|
||||
Чат в админке (экономия ресурсов llama-server): по умолчанию WESP_LLM_CHAT_MAX_TOKENS=512, WESP_LLM_CHAT_TIMEOUT_SEC=45 — см. config.py.
|
||||
Лимит «не чаще раз в N сек» для чата отдельный: WESP_ADMIN_LLM_CHAT_RATE_LIMIT_SEC (по умолчанию 0 — без паузы). Для сводки/ping: WESP_ADMIN_LLM_RATE_LIMIT_SEC (по умолчанию 12).
|
||||
Чат с данными БД в промпте (среда + PRAGMA по файлам SQLite, без строк таблиц ORM): галка в админке или WESP_LLM_CHAT_INCLUDE_DB_CONTEXT=1, ключ llm_chat_db_context в wesp_security_settings.json. Строки справочников — только «Снимок диагностики».
|
||||
|
||||
5) Slim-tools в чате (оркестратор LLM)
|
||||
По умолчанию ВКЛ на боевом config; в pytest выключено (WESP_LLM_TOOLS_ENABLED=0 в TestingConfig).
|
||||
Галка в админке «Инструменты в чате» / ключ llm_tools_enabled в wesp_security_settings.json /
|
||||
переменная WESP_LLM_TOOLS_ENABLED (env имеет приоритет над файлом).
|
||||
|
||||
Модель может вызывать функции OpenAI-формата (нужна поддержка tool_calls в вашей сборке llama-server):
|
||||
slim_sql (только SELECT/WITH … SELECT, bind recipes|reports),
|
||||
slim_extract / slim_summary / slim_xsum / slim_sentiment / slim_emotions для server_log, client_log или raw_text.
|
||||
|
||||
Режим SQL в UI/API:
|
||||
- preview — только EXPLAIN QUERY PLAN (без строк таблиц);
|
||||
- execute — чтение строк в SQLite через подключение mode=ro, лимит строк WESP_LLM_SQL_MAX_ROWS (по умолчанию 200).
|
||||
|
||||
Лимиты и таймауты (см. также config.py):
|
||||
WESP_LLM_TOOL_MAX_ROUNDS, WESP_LLM_LOG_MAX_BYTES, WESP_LLM_LOG_TAIL_LINES.
|
||||
|
||||
Платформы: прод обычно Ubuntu; разработка и тесты — Windows/macOS/Linux. Тесты не требуют живого llama-server.
|
||||
Если ваш llama-server отвечает 400 на поле tools в /v1/chat/completions — отключите инструменты:
|
||||
WESP_LLM_TOOLS_ENABLED=0 или галка в админке.
|
||||
@@ -0,0 +1,18 @@
|
||||
# Создаёт каталоги под WESP_ASSISTANT_DIR (по умолчанию <корень_проекта>/data/assistant).
|
||||
# Запуск из корня репозитория:
|
||||
# powershell -ExecutionPolicy Bypass -File install/assistant/create_layout.ps1
|
||||
# Или:
|
||||
# $env:WESP_ASSISTANT_DIR = "D:\wesp\data\assistant"; .\install\assistant\create_layout.ps1
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path
|
||||
if (-not $env:WESP_ASSISTANT_DIR -or [string]::IsNullOrWhiteSpace($env:WESP_ASSISTANT_DIR)) {
|
||||
$root = Join-Path $repoRoot "data\assistant"
|
||||
} else {
|
||||
$root = $env:WESP_ASSISTANT_DIR
|
||||
}
|
||||
foreach ($sub in @("logs", "cache", "models")) {
|
||||
$p = Join-Path $root $sub
|
||||
New-Item -ItemType Directory -Force -Path $p | Out-Null
|
||||
}
|
||||
Write-Host "OK: $root (подкаталоги logs, cache, models)"
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
# Создаёт каталоги под WESP_ASSISTANT_DIR по умолчанию (/app/assistant).
|
||||
# Запуск на сервере: sudo sh install/assistant/create_layout.sh
|
||||
# Положите файл .gguf в $ROOT/models/ и запустите run_llama_server.sh (llama-server).
|
||||
|
||||
set -e
|
||||
ROOT="${WESP_ASSISTANT_DIR:-/app/assistant}"
|
||||
mkdir -p "$ROOT/logs" "$ROOT/cache" "$ROOT/models"
|
||||
echo "OK: $ROOT/{logs,cache,models}"
|
||||
@@ -0,0 +1,54 @@
|
||||
# Downloads llama-server.exe (Windows x64 CPU) from latest ggml-org/llama.cpp release
|
||||
# into <repo>\tools\llama-server.exe
|
||||
#
|
||||
# From install folder:
|
||||
# powershell -ExecutionPolicy Bypass -File assistant\download_llama_server.ps1
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path
|
||||
$toolsDir = Join-Path $repoRoot "tools"
|
||||
New-Item -ItemType Directory -Force -Path $toolsDir | Out-Null
|
||||
$destExe = Join-Path $toolsDir "llama-server.exe"
|
||||
|
||||
if (Test-Path -LiteralPath $destExe) {
|
||||
Write-Host "Already exists: $destExe" -ForegroundColor Green
|
||||
Write-Host "Delete the file to re-download."
|
||||
exit 0
|
||||
}
|
||||
|
||||
Write-Host "Fetching latest llama.cpp release..." -ForegroundColor Cyan
|
||||
$rel = Invoke-RestMethod -Uri "https://api.github.com/repos/ggml-org/llama.cpp/releases/latest" -Headers @{ "User-Agent" = "Mozilla/5.0 WESP" }
|
||||
$asset = $rel.assets | Where-Object { $_.name -match '^llama-.+-bin-win-cpu-x64\.zip$' } | Select-Object -First 1
|
||||
if (-not $asset) {
|
||||
Write-Error "No llama-*-bin-win-cpu-x64.zip in release $($rel.tag_name). Download manually: https://github.com/ggml-org/llama.cpp/releases"
|
||||
}
|
||||
|
||||
$url = $asset.browser_download_url
|
||||
$zipName = $asset.name
|
||||
$tmp = Join-Path $env:TEMP ("wesp-llama-" + [Guid]::NewGuid().ToString())
|
||||
New-Item -ItemType Directory -Path $tmp -Force | Out-Null
|
||||
$zipPath = Join-Path $tmp $zipName
|
||||
|
||||
try {
|
||||
$mb = [math]::Round($asset.size / 1048576, 1)
|
||||
Write-Host "Downloading $zipName size=${mb}MiB ..." -ForegroundColor Cyan
|
||||
Invoke-WebRequest -Uri $url -OutFile $zipPath -UseBasicParsing
|
||||
Write-Host "Extracting..." -ForegroundColor Cyan
|
||||
Expand-Archive -Path $zipPath -DestinationPath $tmp -Force
|
||||
$found = Get-ChildItem -Path $tmp -Recurse -Filter "llama-server.exe" -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
if (-not $found) {
|
||||
Write-Error "llama-server.exe not found inside zip. Check release layout manually."
|
||||
}
|
||||
Copy-Item -LiteralPath $found.FullName -Destination $destExe -Force
|
||||
$exeDir = $found.Directory.FullName
|
||||
Get-ChildItem -Path $exeDir -Filter "*.dll" -File -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
Copy-Item -LiteralPath $_.FullName -Destination (Join-Path $toolsDir $_.Name) -Force
|
||||
Write-Host "Copied: $($_.Name)" -ForegroundColor DarkGray
|
||||
}
|
||||
Write-Host "OK: $destExe (+ DLLs in tools\)" -ForegroundColor Green
|
||||
Write-Host "Run from install folder: .\assistant\run_llama_server.ps1" -ForegroundColor Yellow
|
||||
Write-Host "Set WESP_ADMIN_LLM_ENABLED=1 and WESP_LLM_BASE_URL=http://127.0.0.1:8080" -ForegroundColor Yellow
|
||||
}
|
||||
finally {
|
||||
Remove-Item -LiteralPath $tmp -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
# Скачивает готовый llama-server.exe (CPU x64) из релиза llama.cpp в <репо>\tools\
|
||||
# Запуск из корня репозитория:
|
||||
# powershell -ExecutionPolicy Bypass -File install\assistant\download_llama_server_windows.ps1
|
||||
#
|
||||
# При смене релиза обновите $ReleaseTag (см. https://github.com/ggml-org/llama.cpp/releases )
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ReleaseTag = "b8864"
|
||||
$ZipName = "llama-$ReleaseTag-bin-win-cpu-x64.zip"
|
||||
$Url = "https://github.com/ggml-org/llama.cpp/releases/download/$ReleaseTag/$ZipName"
|
||||
|
||||
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path
|
||||
$toolsDir = Join-Path $repoRoot "tools"
|
||||
$zipPath = Join-Path $env:TEMP "wesp-$ZipName"
|
||||
$destExe = Join-Path $toolsDir "llama-server.exe"
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $toolsDir | Out-Null
|
||||
|
||||
if (Test-Path -LiteralPath $destExe) {
|
||||
Write-Host "Уже есть: $destExe" -ForegroundColor Green
|
||||
exit 0
|
||||
}
|
||||
|
||||
Write-Host "Загрузка: $Url" -ForegroundColor Cyan
|
||||
Invoke-WebRequest -Uri $Url -OutFile $zipPath -UseBasicParsing
|
||||
|
||||
Write-Host "Распаковка..." -ForegroundColor Cyan
|
||||
$extractDir = Join-Path $env:TEMP "wesp-llama-$ReleaseTag"
|
||||
if (Test-Path $extractDir) { Remove-Item -Recurse -Force $extractDir }
|
||||
Expand-Archive -Path $zipPath -DestinationPath $extractDir -Force
|
||||
|
||||
$found = Get-ChildItem -Path $extractDir -Filter "llama-server.exe" -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
if (-not $found) {
|
||||
Write-Error "В архиве не найден llama-server.exe — скачайте вручную с https://github.com/ggml-org/llama.cpp/releases"
|
||||
}
|
||||
|
||||
Copy-Item -LiteralPath $found.FullName -Destination $destExe -Force
|
||||
Remove-Item $zipPath -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item $extractDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||
|
||||
Write-Host "OK: $destExe" -ForegroundColor Green
|
||||
@@ -0,0 +1,32 @@
|
||||
# Скачивает Qwen2.5-1.5B-Instruct GGUF в WESP_ASSISTANT_DIR\models\
|
||||
# Требуется: pip install -U huggingface_hub
|
||||
#
|
||||
# powershell -ExecutionPolicy Bypass -File install/assistant/download_model.ps1
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path
|
||||
if (-not $env:WESP_ASSISTANT_DIR -or [string]::IsNullOrWhiteSpace($env:WESP_ASSISTANT_DIR)) {
|
||||
$root = Join-Path $repoRoot "data\assistant"
|
||||
} else {
|
||||
$root = $env:WESP_ASSISTANT_DIR
|
||||
}
|
||||
$file = if ($env:WESP_LLM_GGUF_FILE) { $env:WESP_LLM_GGUF_FILE } else { "qwen2.5-1.5b-instruct-q5_k_m.gguf" }
|
||||
$dest = Join-Path $root "models"
|
||||
New-Item -ItemType Directory -Force -Path $dest | Out-Null
|
||||
|
||||
$env:HF_DEST = $dest
|
||||
$env:HF_FILE = $file
|
||||
python -c @"
|
||||
import os, sys
|
||||
from pathlib import Path
|
||||
dest = Path(os.environ['HF_DEST'])
|
||||
file = os.environ['HF_FILE']
|
||||
repo = 'Qwen/Qwen2.5-1.5B-Instruct-GGUF'
|
||||
try:
|
||||
from huggingface_hub import hf_hub_download
|
||||
except ImportError:
|
||||
sys.exit('Установите: pip install -U huggingface_hub')
|
||||
path = hf_hub_download(repo_id=repo, filename=file, local_dir=str(dest))
|
||||
print('OK:', path)
|
||||
"@
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/bin/sh
|
||||
# Скачивает Qwen2.5-1.5B-Instruct GGUF в WESP_ASSISTANT_DIR/models/
|
||||
# Требуется: pip install -U huggingface_hub
|
||||
#
|
||||
# export WESP_ASSISTANT_DIR=/app/assistant
|
||||
# sh install/assistant/download_model.sh
|
||||
#
|
||||
# Переопределить файл:
|
||||
# export WESP_LLM_GGUF_FILE=qwen2.5-1.5b-instruct-q4_k_m.gguf
|
||||
|
||||
set -e
|
||||
ROOT="${WESP_ASSISTANT_DIR:-/app/assistant}"
|
||||
FILE="${WESP_LLM_GGUF_FILE:-qwen2.5-1.5b-instruct-q5_k_m.gguf}"
|
||||
REPO="Qwen/Qwen2.5-1.5B-Instruct-GGUF"
|
||||
export ROOT FILE REPO
|
||||
mkdir -p "$ROOT/models"
|
||||
|
||||
PYBIN="${PYTHON:-python3}"
|
||||
command -v "$PYBIN" >/dev/null 2>&1 || PYBIN=python
|
||||
"$PYBIN" <<'PY'
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
root = Path(os.environ["ROOT"])
|
||||
file = os.environ["FILE"]
|
||||
repo = os.environ["REPO"]
|
||||
dest = root / "models"
|
||||
dest.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
try:
|
||||
from huggingface_hub import hf_hub_download
|
||||
except ImportError:
|
||||
raise SystemExit(
|
||||
"Установите: pip install -U huggingface_hub\n"
|
||||
"Или скачайте файл вручную с https://huggingface.co/%s" % repo
|
||||
)
|
||||
|
||||
path = hf_hub_download(repo_id=repo, filename=file, local_dir=str(dest))
|
||||
print("OK:", path)
|
||||
PY
|
||||
@@ -0,0 +1,61 @@
|
||||
# Run llama-server (llama.cpp) with GGUF.
|
||||
#
|
||||
# From repo root:
|
||||
# powershell -ExecutionPolicy Bypass -File install\assistant\run_llama_server.ps1
|
||||
#
|
||||
# Binary: llama-server.exe in PATH, or $env:LLAMA_SERVER, or tools\llama-server.exe.
|
||||
# WESP: WESP_LLM_BASE_URL=http://127.0.0.1:8080 and WESP_LLM_MODEL = .gguf filename
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path
|
||||
if (-not $env:WESP_ASSISTANT_DIR -or [string]::IsNullOrWhiteSpace($env:WESP_ASSISTANT_DIR)) {
|
||||
$root = Join-Path $repoRoot "data\assistant"
|
||||
} else {
|
||||
$root = $env:WESP_ASSISTANT_DIR
|
||||
}
|
||||
$file = if ($env:WESP_LLM_GGUF_FILE) { $env:WESP_LLM_GGUF_FILE } else { "qwen2.5-1.5b-instruct-q5_k_m.gguf" }
|
||||
$host_ = if ($env:WESP_LLM_HOST) { $env:WESP_LLM_HOST } else { "127.0.0.1" }
|
||||
$port = if ($env:WESP_LLM_PORT) { $env:WESP_LLM_PORT } else { "8080" }
|
||||
$gguf = Join-Path (Join-Path $root "models") $file
|
||||
|
||||
if (-not (Test-Path -LiteralPath $gguf)) {
|
||||
Write-Error "Model file missing: $gguf - run install/assistant/download_model.ps1 or copy a .gguf into models/"
|
||||
}
|
||||
|
||||
$bin = $null
|
||||
if ($env:LLAMA_SERVER -and (Test-Path -LiteralPath $env:LLAMA_SERVER)) {
|
||||
$bin = $env:LLAMA_SERVER
|
||||
}
|
||||
if (-not $bin) {
|
||||
$candidates = @(
|
||||
(Join-Path $repoRoot "tools\llama-server.exe"),
|
||||
(Join-Path $repoRoot "tools\llama-server\llama-server.exe")
|
||||
)
|
||||
foreach ($p in $candidates) {
|
||||
if (Test-Path -LiteralPath $p) { $bin = $p; break }
|
||||
}
|
||||
}
|
||||
if (-not $bin) {
|
||||
$cmd = Get-Command "llama-server" -ErrorAction SilentlyContinue
|
||||
if ($cmd) { $bin = $cmd.Source }
|
||||
}
|
||||
if (-not $bin) {
|
||||
Write-Host ""
|
||||
Write-Host "llama-server.exe not found. Download llama.cpp Windows x64 build:" -ForegroundColor Yellow
|
||||
Write-Host " https://github.com/ggml-org/llama.cpp/releases" -ForegroundColor Cyan
|
||||
Write-Host "Place llama-server.exe in:" -ForegroundColor Yellow
|
||||
Write-Host " $repoRoot\tools\llama-server.exe" -ForegroundColor Cyan
|
||||
Write-Host "Or set: `$env:LLAMA_SERVER = 'C:\path\to\llama-server.exe'" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
exit 1
|
||||
}
|
||||
|
||||
$serverArgs = @(
|
||||
"-m", $gguf,
|
||||
"--host", $host_,
|
||||
"--port", $port,
|
||||
"-c", "8192"
|
||||
)
|
||||
$argLine = $serverArgs -join ' '
|
||||
Write-Host ('Starting llama-server: {0} {1}' -f $bin, $argLine) -ForegroundColor Green
|
||||
& $bin @serverArgs
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/bin/sh
|
||||
# Пример запуска llama-server (сборка llama.cpp) с GGUF Qwen2.5-1.5B-Instruct.
|
||||
# Путь к бинарнику: задайте LLAMA_SERVER или положите llama-server в PATH.
|
||||
#
|
||||
# Переменные:
|
||||
# WESP_ASSISTANT_DIR — корень (по умолчанию /app/assistant)
|
||||
# WESP_LLM_GGUF_FILE — имя файла в models/ (по умолчанию qwen2.5-1.5b-instruct-q5_k_m.gguf)
|
||||
# WESP_LLM_PORT — порт HTTP (по умолчанию 8080)
|
||||
# WESP_LLM_HOST — bind (по умолчанию 127.0.0.1)
|
||||
#
|
||||
# WESP должен смотреть на тот же порт: export WESP_LLM_BASE_URL=http://127.0.0.1:8080
|
||||
# Имя модели в API должно совпадать с -m (часто имя файла .gguf):
|
||||
# export WESP_LLM_MODEL=qwen2.5-1.5b-instruct-q5_k_m.gguf
|
||||
|
||||
set -e
|
||||
ROOT="${WESP_ASSISTANT_DIR:-/app/assistant}"
|
||||
FILE="${WESP_LLM_GGUF_FILE:-qwen2.5-1.5b-instruct-q5_k_m.gguf}"
|
||||
HOST="${WESP_LLM_HOST:-127.0.0.1}"
|
||||
PORT="${WESP_LLM_PORT:-8080}"
|
||||
GGUF="$ROOT/models/$FILE"
|
||||
BIN="${LLAMA_SERVER:-llama-server}"
|
||||
|
||||
if [ ! -f "$GGUF" ]; then
|
||||
echo "Нет файла модели: $GGUF" >&2
|
||||
echo "Запустите install/assistant/download_model.sh или скопируйте .gguf в $ROOT/models/" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec "$BIN" \
|
||||
-m "$GGUF" \
|
||||
--host "$HOST" \
|
||||
--port "$PORT" \
|
||||
-c 8192
|
||||
@@ -0,0 +1,46 @@
|
||||
# Re-download latest llama.cpp win-cpu-x64 zip and copy llama-server.exe + all DLLs into <repo>\tools\
|
||||
# Use when llama-server fails with exit -1073741515 (missing DLL) or after upgrading llama.cpp.
|
||||
#
|
||||
# powershell -ExecutionPolicy Bypass -File install\assistant\sync_llama_tools.ps1
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path
|
||||
$toolsDir = Join-Path $repoRoot "tools"
|
||||
New-Item -ItemType Directory -Force -Path $toolsDir | Out-Null
|
||||
$destExe = Join-Path $toolsDir "llama-server.exe"
|
||||
|
||||
Write-Host "Fetching latest llama.cpp release..." -ForegroundColor Cyan
|
||||
$rel = Invoke-RestMethod -Uri "https://api.github.com/repos/ggml-org/llama.cpp/releases/latest" -Headers @{ "User-Agent" = "Mozilla/5.0 WESP" }
|
||||
$asset = $rel.assets | Where-Object { $_.name -match '^llama-.+-bin-win-cpu-x64\.zip$' } | Select-Object -First 1
|
||||
if (-not $asset) {
|
||||
Write-Error "No llama-*-bin-win-cpu-x64.zip in release $($rel.tag_name). See https://github.com/ggml-org/llama.cpp/releases"
|
||||
}
|
||||
|
||||
$url = $asset.browser_download_url
|
||||
$zipName = $asset.name
|
||||
$tmp = Join-Path $env:TEMP ("wesp-llama-" + [Guid]::NewGuid().ToString())
|
||||
New-Item -ItemType Directory -Path $tmp -Force | Out-Null
|
||||
$zipPath = Join-Path $tmp $zipName
|
||||
|
||||
try {
|
||||
$mb = [math]::Round($asset.size / 1048576, 1)
|
||||
Write-Host "Downloading $zipName (${mb} MiB)..." -ForegroundColor Cyan
|
||||
Invoke-WebRequest -Uri $url -OutFile $zipPath -UseBasicParsing
|
||||
Write-Host "Extracting..." -ForegroundColor Cyan
|
||||
Expand-Archive -Path $zipPath -DestinationPath $tmp -Force
|
||||
$found = Get-ChildItem -Path $tmp -Recurse -Filter "llama-server.exe" -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
if (-not $found) {
|
||||
Write-Error "llama-server.exe not found inside zip."
|
||||
}
|
||||
Copy-Item -LiteralPath $found.FullName -Destination $destExe -Force
|
||||
$exeDir = $found.Directory.FullName
|
||||
$n = 0
|
||||
Get-ChildItem -Path $exeDir -Filter "*.dll" -File -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
Copy-Item -LiteralPath $_.FullName -Destination (Join-Path $toolsDir $_.Name) -Force
|
||||
$n++
|
||||
}
|
||||
Write-Host "OK: $destExe and $n DLL file(s) in tools\" -ForegroundColor Green
|
||||
}
|
||||
finally {
|
||||
Remove-Item -LiteralPath $tmp -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
# Киоск Chromium (локальный терминал)
|
||||
|
||||
Управление: **Система → Киоск: Chromium при входе**.
|
||||
|
||||
## Почему патчится /etc, а не ~/.config
|
||||
|
||||
На Raspberry Pi / Debian с **labwc-pi** используется **`/etc/xdg/labwc/autostart`** (pcmanfm, wf-panel, lxsession). Файл `~komton/.config/labwc/autostart` **не подменяет** системный — отсюда рабочий стол до браузера.
|
||||
|
||||
Режим «Без рабочего стола» (по умолчанию):
|
||||
|
||||
1. Резервная копия → `/etc/xdg/labwc/autostart.wesp-backup`
|
||||
2. Запись минимального autostart: `kanshi` + Chromium → `/starting?next=/scales&theme=dark` (тёмный экран с подписями), затем весы
|
||||
3. Флаги Chromium: `--host-rules` (блок API перевода), policy `URLBlocklist`, профиль без translate
|
||||
4. HTML киоска: `meta google notranslate`, `translate=no`, заголовок `Content-Language: ru`
|
||||
3. При отключении киоска — восстановление из backup
|
||||
|
||||
WESP должен работать **от root** (`myscript.service`), иначе запись в `/etc` не удастся.
|
||||
|
||||
## Установка
|
||||
|
||||
При `sudo bash scripts/install_fresh.sh` или **Админка → Система → «Настроить киоск Pi»** (env + киоск + reboot) переменные ниже попадают в `/etc/wesp/wesp.env` автоматически.
|
||||
|
||||
## Env
|
||||
|
||||
```bash
|
||||
WESP_KIOSK_GUI_USER=komton
|
||||
WESP_KIOSK_TARGET_URL=http://localhost/scales
|
||||
WESP_KIOSK_STARTUP_SCREEN=1
|
||||
WESP_KIOSK_STARTUP_THEME=dark
|
||||
WESP_KIOSK_CHROMIUM=/usr/bin/chromium
|
||||
WESP_KIOSK_SYSTEM_LABWC_AUTOSTART=/etc/xdg/labwc/autostart
|
||||
# WESP_KIOSK_URL= # если задан — переопределяет /starting
|
||||
```
|
||||
|
||||
## Проверка на Pi
|
||||
|
||||
```bash
|
||||
head -5 /etc/xdg/labwc/autostart
|
||||
# Managed by WESP kiosk boot
|
||||
|
||||
head -5 /home/komton/.config/labwc/autostart # не обязателен
|
||||
|
||||
grep pcmanfm /etc/xdg/labwc/autostart || echo "pcmanfm отключён OK"
|
||||
```
|
||||
|
||||
После включения: **sudo reboot**.
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"TranslateEnabled": false,
|
||||
"OfferTranslateEnabled": false,
|
||||
"DefaultNotificationsSetting": 2,
|
||||
"AutofillAddressEnabled": false,
|
||||
"AutofillCreditCardEnabled": false,
|
||||
"BrowserSignin": 0,
|
||||
"SyncDisabled": true,
|
||||
"URLBlocklist": [
|
||||
"*://translate.google.com/*",
|
||||
"*://translate.googleapis.com/*",
|
||||
"*://translate-pa.googleapis.com/*",
|
||||
"*://translation.googleapis.com/*"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
# systemd unit для WESP на Pi (komton): run.py + venv, порт 80.
|
||||
#
|
||||
# sudo cp install/myscript.service.example /etc/systemd/system/myscript.service
|
||||
# # пути подставит scripts/install_fresh.sh
|
||||
# sudo systemctl daemon-reload
|
||||
# sudo systemctl enable --now myscript
|
||||
#
|
||||
# EnvironmentFile=/etc/wesp/wesp.env — см. install/wesp.env.example
|
||||
|
||||
[Unit]
|
||||
Description=WESP feed management server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
Group=root
|
||||
WorkingDirectory=/opt/wesp
|
||||
EnvironmentFile=/etc/wesp/wesp.env
|
||||
ExecStart=/opt/wesp/scripts/wesp_waitress_serve.sh
|
||||
# (отладка) Flask dev-server:
|
||||
# ExecStart=/opt/wes/bin/python3 /opt/wesp/run.py
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,4 @@
|
||||
# Пример sudoers для кнопки «Plymouth → установить» в админке WESP.
|
||||
# Скопируйте в /etc/sudoers.d/wesp-plymouth (visudo -f ...) и замените USER и путь.
|
||||
#
|
||||
# wesp ALL=(root) NOPASSWD: /opt/wesp/scripts/plymouth/install_wesp_theme.sh *
|
||||
@@ -0,0 +1,8 @@
|
||||
[Plymouth Theme]
|
||||
Name=WESP
|
||||
Description=WESP boot splash (800x600 pixel logo animation)
|
||||
ModuleName=script
|
||||
|
||||
[script]
|
||||
ImageDir=/usr/share/plymouth/themes/wesp
|
||||
ScriptFile=/usr/share/plymouth/themes/wesp/wesp.script
|
||||
@@ -0,0 +1,44 @@
|
||||
# WESP Plymouth theme — полноэкранная анимация 800×600 (кадры animation/N.png)
|
||||
|
||||
screen.width = Window.GetWidth();
|
||||
screen.height = Window.GetHeight();
|
||||
|
||||
# Тёмный фон (#0f1114), как data-theme="dark"
|
||||
Window.SetBackgroundTopColor(0.0588, 0.0667, 0.0784);
|
||||
Window.SetBackgroundBottomColor(0.0588, 0.0667, 0.0784);
|
||||
|
||||
animation.length = 0;
|
||||
animation.images = [];
|
||||
|
||||
while (1) {
|
||||
local.path = "animation/" + animation.length + ".png";
|
||||
local.frame = Image(local.path);
|
||||
if (!frame)
|
||||
break;
|
||||
animation.images[animation.length++] = frame;
|
||||
}
|
||||
|
||||
animation.current_frame = 0;
|
||||
animation.sprite = Sprite();
|
||||
|
||||
fun refresh_callback() {
|
||||
if (animation.length == 0)
|
||||
return;
|
||||
|
||||
local.frame = animation.images[animation.current_frame];
|
||||
animation.sprite.SetImage(frame);
|
||||
|
||||
# Кадры 800×600 — на других разрешениях центрируем
|
||||
animation.sprite.SetX((screen.width - frame.GetWidth()) / 2);
|
||||
animation.sprite.SetY((screen.height - frame.GetHeight()) / 2);
|
||||
|
||||
animation.current_frame++;
|
||||
if (animation.current_frame >= animation.length)
|
||||
animation.current_frame = 0;
|
||||
}
|
||||
|
||||
Plymouth.SetRefreshFunction(refresh_callback);
|
||||
|
||||
fun quit_callback() {}
|
||||
|
||||
Plymouth.SetQuitFunction(quit_callback);
|
||||
@@ -0,0 +1,71 @@
|
||||
# /etc/wesp/wesp.env — переменные для systemd.
|
||||
# На Pi (komton): scripts/install_fresh.sh подставит пути и создаст myscript.service.
|
||||
# Вручную: sudo cp install/wesp.env.example /etc/wesp/wesp.env && sudo chmod 600 /etc/wesp/wesp.env
|
||||
|
||||
WESP_CONFIG=production
|
||||
|
||||
# Обязательно смените перед боевым запуском (install_fresh.sh может временно разрешить дефолты):
|
||||
WESP_SECRET_KEY=change-me-in-production
|
||||
WESP_AUTH_LOGIN=admin
|
||||
WESP_AUTH_PASSWORD=admin
|
||||
|
||||
# Каталог кода (WorkingDirectory в systemd)
|
||||
# BASE_DIR выводится из расположения config.py; при нестандартном деплое:
|
||||
# WESP_BASE_DIR=/opt/wesp
|
||||
|
||||
# venv для post_update / OTA (не обязателен для waitress в unit-файле)
|
||||
WESP_VENV=/opt/wes
|
||||
WESP_PYTHON=/opt/wes/bin/python3
|
||||
|
||||
# systemd (Pi komton: myscript.service)
|
||||
WESP_SYSTEMD_UNIT=myscript
|
||||
WESP_ADMIN_RESTART_CMD=systemctl restart myscript
|
||||
WESP_ADMIN_STOP_CMD=systemctl stop myscript
|
||||
|
||||
# После OTA-обновления (post_update вызовет systemctl restart при WESP_SYSTEMD_UNIT)
|
||||
WESP_UPDATE_RESTART_CMD=bash /opt/wesp/scripts/post_update.sh
|
||||
WESP_LISTEN=0.0.0.0:80
|
||||
# Потоки Waitress (по умолчанию у waitress — 4; при нагрузке sync/UI ставьте 16–32)
|
||||
WESP_WAITRESS_THREADS=20
|
||||
WESP_HEALTH_URL=http://127.0.0.1/api/health
|
||||
|
||||
# Автоперезагрузка Pi после киоска / Plymouth / config.txt (секунды задержки)
|
||||
WESP_ADMIN_REBOOT_CMD=sudo /sbin/reboot
|
||||
WESP_AUTO_REBOOT_ENABLED=1
|
||||
WESP_AUTO_REBOOT_DELAY_SEC=3
|
||||
# Задержка reboot после «Настроить киоск Pi» в админке (анимация загрузки)
|
||||
WESP_FULL_SETUP_REBOOT_DELAY_SEC=10
|
||||
WESP_KIOSK_AUTO_REBOOT=1
|
||||
WESP_PI_BOOT_AUTO_REBOOT=1
|
||||
WESP_PLYMOUTH_AUTO_REBOOT=1
|
||||
|
||||
# LAN / mDNS (вкладка «Сеть» в админке; файл data/wesp_network_settings.json)
|
||||
# WESP_NETWORK_LOCAL_HOSTNAME=komton_srv_1.local
|
||||
# WESP_KIOSK_PUBLIC_BASE_URL=http://komton_srv_1.local
|
||||
# WESP_MDNS_ENABLED=1
|
||||
|
||||
# Киоск без GNOME keyring
|
||||
# WESP_KIOSK_HEADLESS=1
|
||||
|
||||
# Ожидание поднятия HTTP перед Chromium (labwc autostart).
|
||||
# Полезно, если киоск иногда показывает «404» сразу после загрузки.
|
||||
# WESP_KIOSK_WAIT_HTTP_MAX_SEC=90
|
||||
# WESP_KIOSK_CHROMIUM_BOOT_DELAY_SEC=2
|
||||
# Скрыть курсор мыши на /starting, /scales и др. (0 = оставить)
|
||||
# WESP_KIOSK_HIDE_CURSOR=1
|
||||
|
||||
# Слабая SD на Raspberry Pi: темп миграций и отложенный фон (секунды; 0 = без паузы)
|
||||
# WESP_MIGRATION_PAUSE_SEC=2
|
||||
# WESP_MIGRATION_PAUSE_BIND_SEC=5
|
||||
# WESP_MIGRATION_PAUSE_STEP_SEC=0.3
|
||||
# На SD при стабильном старте можно попробовать (иначе оставьте пустым — меньше database is locked):
|
||||
# WESP_MIGRATION_SQLITE_JOURNAL_MODE=DELETE
|
||||
# WESP_MIGRATION_SQLITE_SYNCHRONOUS=FULL
|
||||
# WESP_STARTUP_MDNS_DELAY_SEC=15
|
||||
# WESP_STARTUP_SYNC_DELAY_SEC=5
|
||||
# WESP_STARTUP_UPDATE_CHECK_DELAY_SEC=30
|
||||
# WESP_STARTUP_REQUEUE_DELAY_SEC=60
|
||||
# WESP_STARTUP_BOOTSTRAP_DELAY_SEC=3
|
||||
|
||||
# Gitea OTA (опционально)
|
||||
# GITEA_TOKEN=
|
||||
@@ -0,0 +1,25 @@
|
||||
# Пример unit-файла systemd для WESP на Linux.
|
||||
# Код: /opt/wesp venv: /opt/wes (типичная схема komton)
|
||||
#
|
||||
# sudo cp install/wesp.service.example /etc/systemd/system/wesp.service
|
||||
# sudo cp install/wesp.env.example /etc/wesp/wesp.env
|
||||
# sudo nano /etc/wesp/wesp.env # WESP_SECRET_KEY, пароли
|
||||
# sudo systemctl daemon-reload
|
||||
# sudo systemctl enable --now wesp
|
||||
|
||||
[Unit]
|
||||
Description=WESP feed management server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
Group=root
|
||||
WorkingDirectory=/opt/wesp
|
||||
EnvironmentFile=/etc/wesp/wesp.env
|
||||
ExecStart=/opt/wesp/scripts/wesp_waitress_serve.sh
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"organization_name": "",
|
||||
"okpo": "",
|
||||
"farm_name": "",
|
||||
"department": "",
|
||||
"brigade": "",
|
||||
"animal_type": "КРС",
|
||||
"responsible_person": "",
|
||||
"warehouse_keeper": "",
|
||||
"zootechnician": "",
|
||||
"document_number_prefix": "СП20"
|
||||
}
|
||||
Reference in New Issue
Block a user