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:
@@ -1,75 +1,81 @@
|
||||
# gpu-rent
|
||||
|
||||
Прерываемый GPU в [Selectel](https://selectel.ru) + SwarmUI на `http://127.0.0.1:17801`.
|
||||
Модели и ComfyUI живут на сетевом диске — платишь за GPU только пока он поднят.
|
||||
Прерываемый GPU в [Selectel](https://selectel.ru) + [SwarmUI](https://github.com/mcmonkeyprojects/SwarmUI) на `http://127.0.0.1:17801`.
|
||||
|
||||
**Сначала ключи и квота GPU, не `up`.** → [`docs/setup.md`](docs/setup.md)
|
||||
Модели и ComfyUI живут на сетевом диске. **GPU платится только пока жив compute** (`up`…`stop` или idle-killer). Диск — **всегда**, даже после `stop`.
|
||||
|
||||
---
|
||||
|
||||
## Быстрый старт
|
||||
## С нуля → первая картинка
|
||||
|
||||
```powershell
|
||||
.\gpu-rent.ps1 doctor # preflight
|
||||
.\gpu-rent.ps1 flavors # что выберет фоллбек
|
||||
.\gpu-rent.ps1 dry-run # план без create
|
||||
.\gpu-rent.ps1 up --yes # GPU + SwarmUI + seed + туннель :17801
|
||||
.\gpu-rent.ps1 up --yes --no-tunnel # только облако
|
||||
.\gpu-rent.ps1 tunnel --open # снова открыть UI, если туннель закрыли
|
||||
.\gpu-rent.ps1 hold # отложить idle-killer
|
||||
.\gpu-rent.ps1 stop # гасить compute, диски оставить
|
||||
```
|
||||
Делай по порядку. Подробности и скрины панели — в [docs/setup.md](docs/setup.md).
|
||||
|
||||
| # | Шаг | Команда / действие |
|
||||
| --- | --- | --- |
|
||||
| 1 | Python 3.11+, клон репо | — |
|
||||
| 2 | Квота GPU в Selectel (> 0) или тикет | [setup §2](docs/setup.md#2-selectel-проект-и-квота-gpu) |
|
||||
| 3 | Сервисный пользователь + `.env` из RC | [setup §3](docs/setup.md#3-ключи-openstack-это-и-есть-api-key-для-gpu-rent) |
|
||||
| 4 | (Опц.) Civitai token + `models.yaml` | [setup §4](docs/setup.md#4-civitai-api-token-модели) |
|
||||
| 5 | Preflight | `.\gpu-rent.ps1 doctor` |
|
||||
| 6 | Поднять GPU + туннель | `.\gpu-rent.ps1 up --yes` |
|
||||
| 7 | Открыть UI | браузер сам / `.\gpu-rent.ps1 open` |
|
||||
| 8 | Закончить сессию | `.\gpu-rent.ps1 stop` |
|
||||
|
||||
Unix: `./gpu-rent.sh …` (один раз `chmod +x gpu-rent.sh`).
|
||||
|
||||
Лаунчер сам создаёт `.venv` и ставит пакет. Конфиг — **в корне репо**:
|
||||
|
||||
| Файл | Что |
|
||||
| --- | --- |
|
||||
| `.env` | из `env.example` — `OS_*`, Civitai |
|
||||
| `models.yaml` | из `models.example.yaml` — seed с Civitai |
|
||||
| `extensions.yaml` | из `extensions.example.yaml` |
|
||||
| `.gpu-rent/` | state, SSH-ключ (gitignore) |
|
||||
**Не начинай с `up`**, пока `doctor` не зелёный. Ключ панели `X-Token` для этого CLI **не подходит**.
|
||||
|
||||
---
|
||||
|
||||
## Команды
|
||||
## Обычный день
|
||||
|
||||
```powershell
|
||||
.\gpu-rent.ps1 up --yes # GPU + SwarmUI + туннель :17801
|
||||
# работаешь в браузере…
|
||||
# Ctrl+C в терминале = только туннель off, GPU жив
|
||||
.\gpu-rent.ps1 tunnel --open # снова UI
|
||||
.\gpu-rent.ps1 hold # отложить idle-killer
|
||||
.\gpu-rent.ps1 stop # погасить GPU, диски оставить
|
||||
```
|
||||
|
||||
| Важно | |
|
||||
| --- | --- |
|
||||
| `Ctrl+C` на туннеле | **не** гасит GPU |
|
||||
| Простой ~30 мин | idle-killer сам удалит compute |
|
||||
| Preemptible | ~24 ч → `EXPIRED`; `tunnel` / `up` поднимут снова |
|
||||
| Двойной клик лаунчера | задай `GPU_RENT_DEFAULT_ARGS=up --yes` в `gpu-rent.vars` |
|
||||
|
||||
---
|
||||
|
||||
## Команды (кратко)
|
||||
|
||||
| Команда | Смысл |
|
||||
| --- | --- |
|
||||
| `doctor` | Keystone, квота GPU, flavor, Civitai, манифесты |
|
||||
| `flavors` | Живой список по `FLAVOR_PREFERENCE` |
|
||||
| `up` / `up --yes` | Поднять GPU + туннель :17801 (браузер). Ctrl+C = туннель off |
|
||||
| `up --no-tunnel` | Только облако |
|
||||
| `up --no-spot` / `up --flavor ID` | Обычный тариф / явный flavor |
|
||||
| `tunnel` / `tunnel --open` | Повторный проброс; watchdog на EXPIRED |
|
||||
| `open` | Браузер на 17801 |
|
||||
| `hold` / `hold --minutes 90` / `hold --clear` | Пауза idle-killer |
|
||||
| `status` | Nova + диск + killer |
|
||||
| `seed-models` / `seed-extensions` | Докачать на живой диск |
|
||||
| `push` / `push-models` / `pull-output` | Локальные папки ↔ VM |
|
||||
| `resize-data --gb 400` | Диск только вверх |
|
||||
| `setup` | Wizard: `.env`, LLM, watchdog |
|
||||
| `doctor` | Preflight без create |
|
||||
| `flavors` / `dry-run` | Что выберет / план без денег |
|
||||
| `up` / `up --yes` | GPU + seed + туннель |
|
||||
| `up --ollama` | + Ollama (см. [docs/llm.md](docs/llm.md)) |
|
||||
| `tunnel` / `open` | Снова UI / браузер |
|
||||
| `hold` / `status` | Пауза killer / состояние |
|
||||
| `stop` / `destroy --i-understand-data-loss` | Стоп GPU / + диски |
|
||||
| `push` / `pull-output` / `seed-*` | Файлы ↔ VM |
|
||||
|
||||
Полный список: [docs/cli.md](docs/cli.md).
|
||||
|
||||
---
|
||||
|
||||
## Деньги и риски
|
||||
## Конфиг в корне репо
|
||||
|
||||
- **GPU** — пока жив compute (`up`…`stop` или idle-killer).
|
||||
- **Диск data** — **24/7**, даже после `stop`.
|
||||
- Цены в OpenStack API **нет** — смотри панель Selectel.
|
||||
- Preemptible по умолчанию (~24 ч) → `EXPIRED`; `tunnel` сам unshelve, иначе `up`.
|
||||
- Idle-killer: льгота после boot, потом N мин пустой очереди → delete. `hold` откладывает.
|
||||
- `Ctrl+C` на tunnel **не** гасит GPU.
|
||||
| Файл | Что |
|
||||
| --- | --- |
|
||||
| `.env` | из `env.example` — `OS_*`, Civitai (не в git) |
|
||||
| `gpu-rent.vars` | из example — несекретные дефолты, лаунчер |
|
||||
| `models.yaml` / `extensions.yaml` | манифесты (из `*.example.yaml`) |
|
||||
| `Models/` … | локальный push на `up` |
|
||||
| `.gpu-rent/` | state, SSH-ключ (gitignore) |
|
||||
|
||||
---
|
||||
|
||||
## Модели
|
||||
|
||||
1. Civitai-seed по `models.yaml` (нужен `CIVITAI_API_TOKEN`, хост `civitai.red`).
|
||||
2. Локальные `Models/`, `Wildcards/`, `CustomWorkflows/` — непустые едут на каждый `up`.
|
||||
|
||||
Пример в репо уже с Krea 2 + LoRA — скопируй в `models.yaml`.
|
||||
Лаунчер сам создаёт `.venv` и ставит пакет.
|
||||
|
||||
---
|
||||
|
||||
@@ -77,10 +83,11 @@ Unix: `./gpu-rent.sh …` (один раз `chmod +x gpu-rent.sh`).
|
||||
|
||||
| Док | Зачем |
|
||||
| --- | --- |
|
||||
| [docs/setup.md](docs/setup.md) | Ключи, квота, RC |
|
||||
| [docs/spike-notes.md](docs/spike-notes.md) | Чеклист живого spike |
|
||||
| [docs/cli.md](docs/cli.md) | Все команды и `.env` |
|
||||
| [docs/decisions.md](docs/decisions.md) | Продуктовые решения |
|
||||
| [docs/roadmap.md](docs/roadmap.md) | Что готово / что нет |
|
||||
| [docs/setup.md](docs/setup.md) | **Пошаговая подготовка** до первого `up` |
|
||||
| [docs/cli.md](docs/cli.md) | Все команды и переменные |
|
||||
| [docs/llm.md](docs/llm.md) | Ollama / llama.cpp |
|
||||
| [docs/models.md](docs/models.md) | Civitai + папка `Models/` |
|
||||
| [docs/spike-notes.md](docs/spike-notes.md) | Чеклист первого живого прогона |
|
||||
| [docs/README.md](docs/README.md) | Оглавление всего `docs/` |
|
||||
|
||||
MIT. Секреты и веса в git не коммитить.
|
||||
|
||||
+32
-26
@@ -1,39 +1,45 @@
|
||||
# Документация gpu-rent
|
||||
|
||||
CLI-оркестратор: поднимает прерываемый GPU-сервер в Selectel, монтирует постоянные сетевые диски и открывает SwarmUI на `localhost` через SSH-туннель. Платим за GPU только пока идёт сессия; модели и ComfyUI живут на диске.
|
||||
CLI поднимает прерываемый GPU в Selectel, держит SwarmUI/модели на сетевых дисках и открывает UI на `localhost:17801` через SSH-туннель.
|
||||
|
||||
**Код v1 готов.** Живой `up` ждёт квоту GPU и зелёный `doctor`. Старт руками: [setup.md](setup.md), прогон: [spike-notes.md](spike-notes.md).
|
||||
## С чего начать
|
||||
|
||||
## Как читать
|
||||
1. **[setup.md](setup.md)** — квота, ключи, `.env`, первый `doctor` → `up` → `stop`
|
||||
2. Корневой **[README](../README.md)** — быстрый старт и обычный день
|
||||
3. При первом живом GPU заполни **[spike-notes.md](spike-notes.md)** (времена, цены из панели)
|
||||
|
||||
| Документ | Зачем |
|
||||
Дальше — по задаче.
|
||||
|
||||
## По задачам
|
||||
|
||||
| Хочу… | Документ |
|
||||
| --- | --- |
|
||||
| [setup.md](setup.md) | **С чего начать:** квота GPU, сервисный пользователь, Civitai, установка CLI |
|
||||
| [spike-notes.md](spike-notes.md) | Чеклист первого живого прогона |
|
||||
| [concept.md](concept.md) | Задача, границы, модель стоимости |
|
||||
| [decisions.md](decisions.md) | Зафиксированные решения |
|
||||
| [architecture.md](architecture.md) | Компоненты, диски, стейт-машина, idle-killer |
|
||||
| [selectel.md](selectel.md) | Контракт с облаком: auth, API, GPU, preemptible, сеть |
|
||||
| [models.md](models.md) | Первый seed с Civitai, манифест, пропуск дефолта SwarmUI |
|
||||
| [extensions.md](extensions.md) | Git-репы SwarmUI-расширений и ComfyUI nodes |
|
||||
| [autocomplete.md](autocomplete.md) | Word-list промптов, Settings.fds, проверка версии |
|
||||
| [local-folders.md](local-folders.md) | `Models/` / `Wildcards/` / `CustomWorkflows/` push, optional `Output/` pull |
|
||||
| [swarmui.md](swarmui.md) | Нативный install, порт 7801, API, MCP |
|
||||
| [llm.md](llm.md) | Opt-in Ollama / llama.cpp, `ollama-models.yaml`, порты 17811/17812 |
|
||||
| [cli.md](cli.md) | Команды, конфиг, локальный state |
|
||||
| [roadmap.md](roadmap.md) | Порядок реализации |
|
||||
| [open-questions.md](open-questions.md) | Ещё не закрыто |
|
||||
| Понять зачем и сколько стоит | [concept.md](concept.md) |
|
||||
| Все команды и `.env` | [cli.md](cli.md) |
|
||||
| Модели с Civitai / `Models/` | [models.md](models.md) |
|
||||
| Git-расширения SwarmUI/Comfy | [extensions.md](extensions.md) |
|
||||
| Word-list промптов | [autocomplete.md](autocomplete.md) |
|
||||
| Push/pull папок | [local-folders.md](local-folders.md) |
|
||||
| Ollama / llama.cpp | [llm.md](llm.md) |
|
||||
| Как устроены диски и killer | [architecture.md](architecture.md) |
|
||||
| Контракт Selectel | [selectel.md](selectel.md) |
|
||||
| Нативный SwarmUI на VM | [swarmui.md](swarmui.md) |
|
||||
| Зафиксированные решения | [decisions.md](decisions.md) |
|
||||
| Статус реализации | [roadmap.md](roadmap.md) |
|
||||
| Что ещё проверить на spike | [open-questions.md](open-questions.md) |
|
||||
|
||||
## Источники
|
||||
Ревью кода: `docs/reviews/` (для разработки, не для первого запуска).
|
||||
|
||||
## Источники (Selectel / SwarmUI / Civitai)
|
||||
|
||||
Проверено 21 августа 2026:
|
||||
|
||||
- [Selectel: preemptible servers](https://docs.selectel.ru/en/cloud-servers/about/preemptible-servers/)
|
||||
- [Selectel: preemptible](https://docs.selectel.ru/en/cloud-servers/about/preemptible-servers/)
|
||||
- [Selectel: GPU](https://docs.selectel.ru/en/cloud-servers/create/gpus/)
|
||||
- [Selectel: GPU images / drivers](https://docs.selectel.ru/en/cloud-servers/manage/install-gpu-drivers/)
|
||||
- [Selectel: IAM / API auth](https://docs.selectel.ru/en/api/authorization/)
|
||||
- [Selectel: GPU drivers](https://docs.selectel.ru/en/cloud-servers/manage/install-gpu-drivers/)
|
||||
- [Selectel: API auth](https://docs.selectel.ru/en/api/authorization/)
|
||||
- [Selectel: restore preemptible](https://docs.selectel.ru/en/cloud-servers/manage/restore-preemptible-server/)
|
||||
- [SwarmUI](https://github.com/mcmonkeyprojects/SwarmUI) (нативный Linux, порт 7801)
|
||||
- [Civitai Site API](https://developer.civitai.com/) (download + metadata; API есть и на `.red`)
|
||||
- [Civitai: два входа .com / .red](https://civitai.com/articles/28369/two-front-doors-civitaicom-civitaired-and-whats-next)
|
||||
- [SwarmUI](https://github.com/mcmonkeyprojects/SwarmUI)
|
||||
- [Civitai Site API](https://developer.civitai.com/)
|
||||
- [Civitai .com / .red](https://civitai.com/articles/28369/two-front-doors-civitaicom-civitaired-and-whats-next)
|
||||
- [SwarmUI Autocompletions](https://github.com/mcmonkeyprojects/SwarmUI/blob/master/docs/Features/Autocompletions.md)
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
┌─ Selectel, сегмент пула (например ru-7a) ────────────────────┐
|
||||
│ GPU VM (tag preemptible + gpu-rent) │
|
||||
│ SwarmUI : 127.0.0.1:7801 │
|
||||
│ idle-killer: очередь / hold / качалка в UI → delete self │
|
||||
│ application credential (compute delete/shelve only) │
|
||||
│ idle-killer: очередь / hold / LLM busy → delete this server │
|
||||
│ app cred: DELETE/GET только этот server_id (fail closed) │
|
||||
│ optional: Ollama :11434 / llama.cpp :8080 (туннель 17811/12)│
|
||||
│ │
|
||||
│ boot volume (network) ОС + NVIDIA + SwarmUI нативно + snapshot │
|
||||
│ data volume (network) Models, Output, Data, workflows │
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
Официальная инструкция: [docs/Features/Autocompletions.md](https://github.com/mcmonkeyprojects/SwarmUI/blob/master/docs/Features/Autocompletions.md).
|
||||
|
||||
Нужен word-list (`.csv` / `.txt`) в `Data/Autocompletions` и выбранный источник в настройках пользователя. Без файла в UI нечего выбирать.
|
||||
**По умолчанию включено** (`AUTOCOMPLETE_ENABLED=true`): на bootstrap CLI качает `danbooru.csv` и прописывает источник в Settings **до** первого старта SwarmUI. Ничего руками делать не нужно.
|
||||
|
||||
Выключить: `AUTOCOMPLETE_ENABLED=false` в `.env`.
|
||||
|
||||
## Поведение gpu-rent
|
||||
|
||||
|
||||
+135
-113
@@ -1,144 +1,153 @@
|
||||
# CLI и конфигурация
|
||||
|
||||
Имя команды: `gpu-rent`. Windows и Linux. Лицензия MIT, в репозитории нет секретов и имён личных чекпоинтов.
|
||||
Имя команды: `gpu-rent`. Лаунчеры: `gpu-rent.bat` / `.\gpu-rent.ps1` / `./gpu-rent.sh` (создают `.venv` и вызывают `python -m gpu_rent`).
|
||||
|
||||
Без аргументов CLI показывает **help**. Поднять GPU с двойного клика: в `gpu-rent.vars` → `GPU_RENT_DEFAULT_ARGS=up --yes`.
|
||||
|
||||
---
|
||||
|
||||
## Типичные сценарии
|
||||
|
||||
### Первый раз
|
||||
|
||||
```text
|
||||
gpu-rent doctor
|
||||
gpu-rent up --yes
|
||||
# … работа …
|
||||
gpu-rent stop
|
||||
```
|
||||
|
||||
Подготовка ключей: [setup.md](setup.md).
|
||||
|
||||
### Обычная сессия
|
||||
|
||||
```text
|
||||
gpu-rent up --yes
|
||||
# Ctrl+C → туннель off, GPU жив
|
||||
gpu-rent tunnel --open
|
||||
gpu-rent hold
|
||||
gpu-rent stop
|
||||
```
|
||||
|
||||
### Только облако (туннель позже)
|
||||
|
||||
```text
|
||||
gpu-rent up --yes --no-tunnel
|
||||
gpu-rent tunnel --open
|
||||
```
|
||||
|
||||
### LLM рядом со SwarmUI
|
||||
|
||||
```text
|
||||
gpu-rent setup # или
|
||||
gpu-rent up --yes --ollama
|
||||
```
|
||||
|
||||
Подробности: [llm.md](llm.md).
|
||||
|
||||
---
|
||||
|
||||
## Команды
|
||||
|
||||
| Команда | Поведение |
|
||||
| --- | --- |
|
||||
| `gpu-rent flavors` | Скан `SCAN_POOLS` (ru-6 multizone…) × `FLAVOR_PREFERENCE`, затем список в текущем `OS_REGION_NAME` |
|
||||
| `gpu-rent doctor` | Preflight **без** create: Keystone, квота GPU, flavor в AZ, диски, Civitai token+`.red`, манифесты, SSH-ключ. Код выхода ≠ 0, если сессию нельзя начать |
|
||||
| `gpu-rent setup` | Wizard: `.env`/манифесты, `LLM_RUNTIME`, пресет ollama-models, опционально local-watchdog |
|
||||
| `gpu-rent up` / `up --yes` | Preflight → create/unshelve → bootstrap → git update → optional LLM → **туннель** `localhost:17801`; Ctrl+C закрывает туннель |
|
||||
| `gpu-rent up --ollama` / `--llamacpp` / `--llm …` | Поднять LLM рядом со SwarmUI (см. [llm.md](llm.md)) |
|
||||
| `gpu-rent up --no-update` | Без `git pull` SwarmUI и extensions (только недостающие clone) |
|
||||
| `gpu-rent up --no-tunnel` | Только облако + bootstrap, без локального проброса |
|
||||
| `gpu-rent up --no-spot` | Обычный (не preemptible) сервер |
|
||||
| `gpu-rent up --flavor … --yes` | Без вопросов. `--flavor` бьёт список фоллбека. `--yes` без `--flavor` берёт первый доступный из `FLAVOR_PREFERENCE` |
|
||||
| `gpu-rent tunnel` | Повторный SSH-проброс, если `up --no-tunnel` или туннель уже закрыли. Ctrl+C = закрыть туннель, GPU **оставить** |
|
||||
| `gpu-rent tunnel --open` | Туннель + сразу открыть браузер на 17801 |
|
||||
| `gpu-rent open` | Открыть браузер на `http://127.0.0.1:17801`. Туннель уже должен слушать порт |
|
||||
| `gpu-rent status` | State + Nova + диск used/free + ₽/час + сколько до 24 ч preempt + killer/hold. Туннель не обязателен |
|
||||
| `gpu-rent hold` | Отложить idle-killer на `IDLE_MINUTES` от сейчас |
|
||||
| `gpu-rent hold --minutes 90` | Hold до now+90 мин (заменяет предыдущий, не складывает) |
|
||||
| `gpu-rent hold --until <ISO>` | Hold до абсолютного времени |
|
||||
| `gpu-rent hold --clear` | Снять hold |
|
||||
| `gpu-rent stop` | Optional pull Output → удалить compute и FIP, диски оставить. С любой машины с `.env` |
|
||||
| `gpu-rent stop --no-pull` | Не тянуть Output, даже если `PULL_OUTPUT=true` |
|
||||
| `gpu-rent destroy` | `stop` + диски, только `--i-understand-data-loss` |
|
||||
| `gpu-rent logs` | journalctl swarmui / cloud-init по SSH |
|
||||
| `gpu-rent ssh` | Оболочка на VM |
|
||||
| `gpu-rent seed-models` | Докачать новые строки манифеста Civitai на уже существующий диск |
|
||||
| `gpu-rent push` | Инкремент `Models/` + `Wildcards/` + `CustomWorkflows/` (пустые skip) |
|
||||
| `gpu-rent push-models` | Только `./Models` |
|
||||
| `gpu-rent pull-output` | Забрать новые файлы с VM `Output/` в `./Output` (сервер не чистим) |
|
||||
| `gpu-rent seed-extensions` | Доклонировать/обновить git-репы; если VM жива — `systemctl restart swarmui` |
|
||||
| `gpu-rent resize-data --gb 400` | Увеличить data volume вверх (Selectel online resize). Вниз нельзя |
|
||||
| `gpu-rent watchdog install` | Локальный тик (Task Scheduler / systemd user / launchd): аварийное закрытие туннеля → `stop` после grace |
|
||||
| `gpu-rent watchdog uninstall` | Снять локальный сервис |
|
||||
| `gpu-rent watchdog status` | Установлен ли сервис + local lease |
|
||||
| `gpu-rent watchdog tick` | Один тик (для планировщика; `--dry-run` без delete) |
|
||||
| `gpu-rent setup` | Wizard: манифесты, `LLM_RUNTIME`, пресет ollama-models, опционально local-watchdog |
|
||||
| `gpu-rent doctor` | Preflight **без** create. Exit ≠ 0 → сессию начинать нельзя |
|
||||
| `gpu-rent flavors` | Скан `SCAN_POOLS` × `FLAVOR_PREFERENCE`, список в текущем регионе |
|
||||
| `gpu-rent dry-run` | План без mutating-вызовов |
|
||||
| `gpu-rent up` / `up --yes` | Create/unshelve → bootstrap → optional LLM → **туннель** `:17801`. Ctrl+C = туннель off |
|
||||
| `gpu-rent up -v` / `--verbose` | Полная таблица doctor на `up` (по умолчанию кратко) |
|
||||
| `gpu-rent up --ollama` / `--llamacpp` / `--llm …` | LLM рядом со SwarmUI |
|
||||
| `gpu-rent up --no-update` | Без `git pull` SwarmUI/extensions (только недостающие clone) |
|
||||
| `gpu-rent up --no-tunnel` | Только облако |
|
||||
| `gpu-rent up --no-spot` | Не preemptible |
|
||||
| `gpu-rent up --flavor ID --yes` | Явный flavor; без `--flavor` + `--yes` — первый доступный из `FLAVOR_PREFERENCE` |
|
||||
| `gpu-rent tunnel` / `tunnel --open` | Повторный проброс; `--open` сразу браузер. Ctrl+C GPU **не** гасит |
|
||||
| `gpu-rent open` / `open --llm` | Браузер на SwarmUI / LLM-порт (туннель уже должен слушать) |
|
||||
| `gpu-rent status` | State, Nova, диск, killer/hold, LLM, local-watchdog |
|
||||
| `gpu-rent hold` / `--minutes N` / `--until ISO` / `--clear` | Пауза idle-killer (нужны живая VM + SSH) |
|
||||
| `gpu-rent stop` / `stop --no-pull` | Удалить compute (+ FIP), диски оставить; optional pull Output |
|
||||
| `gpu-rent destroy --i-understand-data-loss` | `stop` + диски |
|
||||
| `gpu-rent logs` | journalctl swarmui / cloud-init |
|
||||
| `gpu-rent ssh` | Оболочка на VM |
|
||||
| `gpu-rent seed-models` | Докачать новые строки Civitai-манифеста на живой диск |
|
||||
| `gpu-rent seed-extensions` | Доклонировать/обновить git-репы; restart swarmui |
|
||||
| `gpu-rent push` / `push-models` | Локальные деревья → VM |
|
||||
| `gpu-rent pull-output` | VM `Output/` → `./Output` |
|
||||
| `gpu-rent resize-data --gb 400` | Data volume **только вверх** |
|
||||
| `gpu-rent watchdog install` / `uninstall` / `status` / `tick` | Локальный safety-net (см. ниже) |
|
||||
|
||||
Второй `up` при живой VM: не создавать второй GPU; сделать autocomplete-check и **push** локальных папок; если `PULL_OUTPUT` — подтянуть Output; напомнить про `tunnel` / `stop`. `--adopt` если нашли тег без state.
|
||||
Второй `up` при живой VM: не создаёт второй GPU; autocomplete-check + push локальных папок; при `PULL_OUTPUT` — подтянуть Output. `--adopt` — подхватить тег без state.
|
||||
|
||||
Нет команды `generate`. Нет зеркала каталога локального SwarmUI — только папки приложения, см. [local-folders.md](local-folders.md).
|
||||
Нет команды `generate`. Локальный SwarmUI целиком не зеркалируем — только папки приложения ([local-folders.md](local-folders.md)).
|
||||
|
||||
Сейчас в коде: после seed/`start swarmui` — idle-killer, Idle backend, boot snapshot, toast/MCP-сниппет, `resize-data`. `tunnel` с Nova-watchdog: EXPIRED → unshelve + reconnect. Опционально `watchdog install` — локальный safety net. UX-полировка flavors/цен ещё впереди.
|
||||
|
||||
## Local watchdog
|
||||
|
||||
Опционально. **Idle-killer на VM** остаётся основным: ноут можно закрыть, GPU живёт до простоя. Local watchdog — если хочешь гасить GPU при «убили окно / ребут» без `stop`.
|
||||
|
||||
1. `gpu-rent watchdog install` (раз на машине, в корне репо).
|
||||
2. Пока крутится `up`/`tunnel`, пишется heartbeat в `.gpu-rent/local-lease.json`.
|
||||
3. **Ctrl+C** → detach, GPU **не** трогаем (как раньше).
|
||||
4. Процесс умер / ребут → через `LOCAL_WATCHDOG_GRACE_MINUTES` (дефолт 10) тик вызывает `stop` (диски остаются).
|
||||
5. `gpu-rent stop` чистит lease сам.
|
||||
|
||||
Без `install` поведение прежнее. Без открытого туннеля (`up --no-tunnel`) lease не вооружается — работает только VM idle-killer.
|
||||
---
|
||||
|
||||
## `doctor`
|
||||
|
||||
Не создаёт сервер, не тратит GPU. Нужен до первого `up` и когда «вчера работало».
|
||||
Не создаёт сервер и не тратит GPU. Нужен до первого `up` и когда «вчера работало».
|
||||
|
||||
Проверки по порядку, все печатаются (не падать на первой, собрать отчёт):
|
||||
Проверки (все печатаются в отчёт):
|
||||
|
||||
1. Читается `.env`, обязательные `OS_*`.
|
||||
2. Keystone: токен выдаётся.
|
||||
3. Квота GPU > 0; иначе текст «напишите в поддержку Selectel».
|
||||
4. В сегменте есть хотя бы один flavor из `FLAVOR_PREFERENCE` (не `disabled`).
|
||||
5. Volume type сегмента существует; data 100 GB влезает в квоту дисков.
|
||||
6. SSH-ключ: есть `<repo>/.gpu-rent/id_ed25519` или CLI сможет его создать.
|
||||
7. Если задан `CIVITAI_API_TOKEN` — HEAD/лёгкий запрос к `CIVITAI_API_HOST` (дефолт `.red`); манифест парсится.
|
||||
8. `extensions.yaml` парсится, если файл есть.
|
||||
9. Локальные папки: предупреждение, если `Models/` огромный относительно свободного места на будущем диске.
|
||||
1. Читается `.env`, обязательные `OS_*`
|
||||
2. Keystone выдаёт токен
|
||||
3. Квота GPU > 0 (иначе текст про поддержку Selectel)
|
||||
4. В сегменте есть flavor из `FLAVOR_PREFERENCE`
|
||||
5. Volume type / место под data ~100 GB
|
||||
6. SSH-ключ есть или будет создан в `.gpu-rent/`
|
||||
7. Если есть `CIVITAI_API_TOKEN` — доступ к API-хосту; манифест парсится
|
||||
8. `extensions.yaml` парсится, если есть
|
||||
9. Предупреждения по огромным локальным `Models/`
|
||||
|
||||
Exit 0 — можно `up`. Exit 1 — нельзя, причина в отчёте.
|
||||
Exit 0 → можно `up`. Exit 1 → причина в таблице / кратком списке на `up`.
|
||||
|
||||
## `status`
|
||||
---
|
||||
|
||||
Без туннеля. Если SSH есть — ещё диск и killer; если нет — только OpenStack.
|
||||
## `status` и `hold`
|
||||
|
||||
| Поле | Откуда |
|
||||
| --- | --- |
|
||||
| Фаза state / Nova status | `state.json` + compute |
|
||||
| Flavor, ₽/час | extra specs / оценка с spike; нет цены в API — «см. панель», не выдумывать |
|
||||
| Preempt window | create/unshelve timestamp + 24 ч; «осталось Hh Mm» или «не preemptible» |
|
||||
| Data disk used/free | SSH `df` на `/mnt/swarm_data` |
|
||||
| Idle-killer | armed / grace до … / hold до … / busy (очередь или качалка) / слеп (нет кредов) |
|
||||
| Туннель | слушает ли локальный 17801 |
|
||||
| Snapshot boot | есть ли `gpu-rent-boot-ok` |
|
||||
**status** — без туннеля. При SSH: ещё `df` диска и состояние killer; иначе только OpenStack + локальный state.
|
||||
|
||||
## `hold`
|
||||
**hold** пишет на VM `/mnt/swarm_data/.gpu-rent-hold-until`. Killer не удаляет compute, пока `now < ts`. Без живой VM — ошибка (не «запомню на потом»). После unshelve hold на диске сохраняется.
|
||||
|
||||
Пишет на VM `/mnt/swarm_data/.gpu-rent-hold-until` (unix ts). Killer не удаляет compute, пока `now < ts`. Нужен SSH и живой сервер.
|
||||
---
|
||||
|
||||
Без VM — ошибка, не «запомню на потом». После `unshelve` hold-файл на диске сохраняется.
|
||||
## Local watchdog (опционально)
|
||||
|
||||
## Что печатать при `up`
|
||||
Основной авто-stop — **idle-killer на VM** (ноут можно закрыть). Local watchdog — если хочешь гасить GPU при «убили окно / ребут» без `stop`.
|
||||
|
||||
1. Preflight (тот же набор, что `doctor`, можно вызвать его внутри).
|
||||
2. Если квота 0 — стоп с текстом «напишите в поддержку Selectel», не traceback.
|
||||
3. Выбранный flavor (или фоллбек) + оценка: GPU ₽/час, диск ₽/мес, preemptible 24 ч, idle-killer через N мин.
|
||||
4. Фазы: provisioning → bootstrap → clone extensions → autocomplete → seed Civitai → push локальных папок → waiting UI → backend Idle.
|
||||
5. Если `NOTIFY_READY` — Windows toast + звук (и строка в лог).
|
||||
6. Готово:
|
||||
1. `gpu-rent watchdog install` (раз на машине, из корня репо)
|
||||
2. Пока крутится `up`/`tunnel`, пишется heartbeat `.gpu-rent/local-lease.json`
|
||||
3. **Ctrl+C** → detach, GPU **не** трогаем
|
||||
4. Процесс умер / ребут → через `LOCAL_WATCHDOG_GRACE_MINUTES` (дефолт 10) → `stop`
|
||||
5. `gpu-rent stop` чистит lease сам
|
||||
|
||||
```text
|
||||
SwarmUI на VM: 127.0.0.1:7801 (только через туннель)
|
||||
Локально: gpu-rent tunnel
|
||||
Браузер: gpu-rent open → http://127.0.0.1:17801
|
||||
API: http://127.0.0.1:17801/API/
|
||||
MCP: http://127.0.0.1:17801/mcp
|
||||
Hold killer: gpu-rent hold
|
||||
Стоп GPU: gpu-rent stop
|
||||
```
|
||||
Без `install` поведение прежнее. При `up --no-tunnel` lease не вооружается.
|
||||
|
||||
Сниппет MCP для Cursor — в stdout, `mcp.json` не редактировать.
|
||||
---
|
||||
|
||||
## Конфигурация
|
||||
|
||||
| Файл | Назначение |
|
||||
| --- | --- |
|
||||
| `.env` | Секреты и OpenStack (`OS_*`, токены). Не в git |
|
||||
| `gpu-rent.vars` | Параметры запуска / несекретные дефолты. Читают `gpu-rent.ps1` / `.sh` / `.bat` и CLI. Пример: `gpu-rent.vars.example` |
|
||||
| `models.yaml` / `extensions.yaml` | Манифесты |
|
||||
| `gpu-rent.vars` | Несекретные дефолты; читают лаунчеры и CLI. Пример: `gpu-rent.vars.example` |
|
||||
| `models.yaml` / `extensions.yaml` / `ollama-models.yaml` | Манифесты |
|
||||
|
||||
`.env` в корне репозитория (рядом с `env.example`).
|
||||
|
||||
В `gpu-rent.vars` (создаётся из example при первом запуске):
|
||||
### Лаунчер (`gpu-rent.vars`)
|
||||
|
||||
```env
|
||||
# Двойной клик / запуск лаунчера без аргументов (CLI сам по себе показывает help):
|
||||
# Двойной клик / запуск без аргументов у .ps1/.bat/.sh:
|
||||
GPU_RENT_DEFAULT_ARGS=up --yes
|
||||
|
||||
# Дописать ко всем вызовам:
|
||||
# GPU_RENT_EXTRA_ARGS=--no-update
|
||||
|
||||
UPDATE_GIT=true
|
||||
LLM_RUNTIME=none
|
||||
```
|
||||
|
||||
### Основные переменные (`.env` / vars)
|
||||
|
||||
```env
|
||||
OS_AUTH_URL=https://cloud.api.selcloud.ru/identity/v3
|
||||
OS_USER_DOMAIN_NAME=
|
||||
@@ -150,6 +159,8 @@ GPU_RENT_AZ=ru-7a
|
||||
|
||||
SSH_PRIVATE_KEY_PATH=
|
||||
SSH_USER=ubuntu
|
||||
# Пусто = авто /32 твоего IP. Для spike/WARP иногда 0.0.0.0/0:
|
||||
GPU_RENT_SSH_CIDR=
|
||||
|
||||
BOOT_VOLUME_ID=
|
||||
DATA_VOLUME_ID=
|
||||
@@ -167,46 +178,57 @@ EXTENSIONS_MANIFEST=
|
||||
GIT_TOKEN=
|
||||
|
||||
AUTOCOMPLETE_ENABLED=true
|
||||
AUTOCOMPLETE_GITHUB_REPO=DominikDoom/a1111-sd-webui-tagcomplete
|
||||
AUTOCOMPLETE_GITHUB_PATH=tags/danbooru.csv
|
||||
AUTOCOMPLETE_GITHUB_REF=main
|
||||
AUTOCOMPLETE_FILENAME=danbooru.csv
|
||||
|
||||
SWARMUI_LOCAL_PORT=17801
|
||||
LLM_RUNTIME=none
|
||||
OLLAMA_LOCAL_PORT=17811
|
||||
LLAMACPP_LOCAL_PORT=17812
|
||||
UPDATE_GIT=true
|
||||
|
||||
DEFAULT_FLAVOR_ID=
|
||||
FLAVOR_PREFERENCE=4090-24,4090-48,a5000,a100-40
|
||||
FLAVOR_FALLBACK=true
|
||||
SCAN_POOLS=ru-6,ru-7
|
||||
DEFAULT_SPOT=true
|
||||
KEEP_FLOATING_IP=false
|
||||
IDLE_MINUTES=30
|
||||
IDLE_GRACE_MINUTES=45
|
||||
# LOCAL_WATCHDOG_GRACE_MINUTES=10
|
||||
PULL_OUTPUT=false
|
||||
NOTIFY_READY=true
|
||||
```
|
||||
|
||||
Flavor id в git не хардкодить. `FLAVOR_PREFERENCE` — ярлыки; реальные uuid резолвятся в сегменте. `FLAVOR_FALLBACK=false` — только `DEFAULT_FLAVOR_ID` / `--flavor`, без следующего в списке.
|
||||
Flavor uuid в git не хардкодить. `FLAVOR_FALLBACK=false` — только `DEFAULT_FLAVOR_ID` / `--flavor`.
|
||||
|
||||
Application credential для idle-killer CLI создаёт при bootstrap и кладёт на VM; в `.env` ноутбука его можно не дублировать.
|
||||
Application credential для idle-killer CLI создаёт на `up` (узко: DELETE/GET **этого** server) и отзывает на `stop`. В `.env` ноутбука дублировать не нужно.
|
||||
|
||||
Пути локальных папок по умолчанию — каталоги в корне приложения: `Models/`, `Wildcards/`, `CustomWorkflows/`, `Output/`.
|
||||
Пути локальных папок по умолчанию: `Models/`, `Wildcards/`, `CustomWorkflows/`, `Output/` в корне репо.
|
||||
|
||||
Полный шаблон: [`env.example`](../env.example).
|
||||
|
||||
---
|
||||
|
||||
## State
|
||||
|
||||
`<repo>/.gpu-rent/state.json` — ids, фаза (`idle` / `ready_cloud` / `ready_tunneled` / …), flavor, timestamps create/unshelve (для окна 24 ч). Без паролей. Lockfile там же, чтобы два `up` не создали два сервера.
|
||||
`<repo>/.gpu-rent/state.json` — ids, фаза, timestamps (окно preempt 24 ч). Без паролей. Lockfile рядом — два `up` не создадут два сервера.
|
||||
|
||||
---
|
||||
|
||||
## Ожидание готовности
|
||||
|
||||
Потолок ~20–40 мин на первый bootstrap, ~5–10 мин на unshelve:
|
||||
Ориентир: **~20–40 мин** первый bootstrap, **~5–10 мин** unshelve.
|
||||
|
||||
Порядок готовности:
|
||||
|
||||
1. Nova `ACTIVE`
|
||||
2. TCP 22
|
||||
3. SSH: cloud-init или `systemctl is-active swarmui`
|
||||
4. HTTP `http://127.0.0.1:7801` **на VM** (через SSH), не путать с локальным 7801
|
||||
5. Для MCP/API / toast — дождаться Idle backend.
|
||||
2. TCP 22 / SSH
|
||||
3. HTTP `http://127.0.0.1:7801` **на VM** (через SSH)
|
||||
4. Backend Idle → toast (если `NOTIFY_READY`) + access-card
|
||||
|
||||
## Windows
|
||||
Локальный порт UI: **17801** (на VM по-прежнему 7801 на loopback).
|
||||
|
||||
Лаунчеры в корне: `gpu-rent.bat` (cmd / двойной клик), `.\gpu-rent.ps1` (PowerShell). На Unix — `./gpu-rent.sh`. Они создают `.venv` в репозитории и вызывают `python -m gpu_rent`. Ключ генерирует CLI: `<repo>/.gpu-rent/id_ed25519`.
|
||||
---
|
||||
|
||||
`NOTIFY_READY`: toast через WinRT / `win10toast` (что заработает на 10/11 без админ-прав) + системный звук. Если toast недоступен — только звук и лог, не падать.
|
||||
## Windows / notify
|
||||
|
||||
`NOTIFY_READY`: toast + системный звук, когда backend Idle. Если toast недоступен — только звук и лог, без падения CLI.
|
||||
|
||||
@@ -24,6 +24,7 @@ GPU в облаке дорогой. Веса для генерации карт
|
||||
- Первый clone git-реп расширений SwarmUI и ComfyUI nodes из `extensions.yaml`.
|
||||
- Autocomplete: word-list в `Data/Autocompletions` до старта UI, на каждом `up` проверка новой версии.
|
||||
- `doctor` до create; фоллбек flavor; `hold` для idle-killer; toast когда backend Idle; `open` на 17801.
|
||||
- Opt-in LLM (Ollama / llama.cpp) через `setup` / `--ollama` / `LLM_RUNTIME`.
|
||||
- `status`: диск, ₽/час, окно preempt 24 ч, состояние killer.
|
||||
- Snapshot boot-диска после первого удачного bootstrap.
|
||||
|
||||
|
||||
+6
-4
@@ -32,11 +32,13 @@
|
||||
| Wildcards / workflows | Как Models: пустые папки не трогаем; появился файл — push на `up` |
|
||||
| Idle hold | `gpu-rent hold [--minutes N]` сдвигает дедлайн killer. Качалка моделей в UI SwarmUI тоже считается занятостью (сигнал уточнить на spike) |
|
||||
| Doctor | `gpu-rent doctor` без create: Keystone, квота GPU, flavor, диск, Civitai token+`.red`, манифесты, SSH-ключ |
|
||||
| Bare CLI | Без аргументов — help. `up` только явно или через `GPU_RENT_DEFAULT_ARGS` у лаунчера |
|
||||
| Flavor fallback | Список предпочтений (4090 → A5000 → …). Нет первого — предложить следующий с ценой, `--yes` берёт первый доступный из списка |
|
||||
| Готово | Windows toast + звук (и лог), когда backend Idle |
|
||||
| Status | Диск used/free, ₽/час, сколько до 24 ч preempt, состояние killer / hold |
|
||||
| Готово | Windows toast + звук (и лог), когда backend Idle; access-card с URL/MCP |
|
||||
| Status | Диск used/free, ₽/час, сколько до 24 ч preempt, состояние killer / hold / LLM |
|
||||
| Boot snapshot | После первого удачного bootstrap — один snapshot boot volume `gpu-rent-boot-ok`; следующие create могут идти с него |
|
||||
| Open | `gpu-rent open` / флаг после `tunnel`: браузер на 17801 |
|
||||
| Open | `gpu-rent open` / `open --llm` / флаг после `tunnel`: браузер на 17801 / LLM-порт |
|
||||
| App cred idle-killer | Только этот `server_id`; fail closed без unrestricted; revoke на stop |
|
||||
|
||||
Следствия, которые из этого вытекают и тоже считаются принятыми:
|
||||
|
||||
@@ -44,4 +46,4 @@
|
||||
2. **Локальный SwarmUI на 7801 не трогаем.** Туннель по умолчанию на **17801** (на VM по-прежнему 7801 на loopback).
|
||||
3. **`Ctrl+C` на туннеле не удаляет VM.** Иначе «закрыл ноут» невозможно. Чтобы убить GPU — `gpu-rent stop` или простой. При установленном **local-watchdog** Ctrl+C по-прежнему detach; убийство процесса/ребут без detach → stop после grace.
|
||||
4. Idle-killer на VM **не может** быть `shutdown -h`: у Selectel остановленная изнутри VM часто продолжает тарифицировать ресурсы. Нужен вызов OpenStack: удалить **этот** compute, диски оставить.
|
||||
5. Для этого на VM — OpenStack **application credential** с правом удалить/shelve сервер в проекте (не пароль владельца аккаунта). Компрометация SwarmUI в худшем случае сносит GPU-сессию, а не создаёт новые дорогие машины, если роль без `compute:create`.
|
||||
5. Для этого на VM — OpenStack **application credential** с правом DELETE/GET **только этого** compute (не `/servers/*`, не unrestricted fallback). На `stop`/`destroy` cred отзывается. Компрометация SwarmUI в худшем случае сносит текущую GPU-сессию, а не произвольные машины проекта.
|
||||
|
||||
+27
-4
@@ -1,4 +1,14 @@
|
||||
# Расширения: git-репы на первый bootstrap
|
||||
# Расширения: git-репы на bootstrap
|
||||
|
||||
## Быстрый старт
|
||||
|
||||
```powershell
|
||||
copy extensions.example.yaml extensions.yaml
|
||||
# добавь url реп (публичные — без GIT_TOKEN)
|
||||
.\gpu-rent.ps1 up --yes
|
||||
# доклонировать на уже живой VM:
|
||||
.\gpu-rent.ps1 seed-extensions
|
||||
```
|
||||
|
||||
Срабатывает на каждом `up` (и `seed-extensions`):
|
||||
|
||||
@@ -23,15 +33,28 @@
|
||||
|
||||
```yaml
|
||||
swarmui:
|
||||
- url: https://gitea.hsrv.site/mrleo1nid/swarm-assistent.git
|
||||
ref: main
|
||||
dir: swarm-assistent
|
||||
requires: ollama # none | ollama | llamacpp | any-llm; default none = always
|
||||
- url: https://github.com/example/SwarmUI-SomeExt.git
|
||||
ref: main # ветка, тег или commit SHA; по умолчанию HEAD default branch
|
||||
dir: SomeExt # имя папки; по умолчанию последний сегмент URL без .git
|
||||
ref: main
|
||||
dir: SomeExt
|
||||
comfy:
|
||||
- url: https://github.com/example/ComfyUI-Something.git
|
||||
ref: v1.2.0
|
||||
```
|
||||
|
||||
В git репозитория gpu-rent не коммитить рабочий список с приватными URL. В примере — вымышленные репы.
|
||||
| `requires` | Когда клонировать |
|
||||
| --- | --- |
|
||||
| `none` (или поле отсутствует) | всегда |
|
||||
| `ollama` | только при `LLM_RUNTIME=ollama` |
|
||||
| `llamacpp` | только при `LLM_RUNTIME=llamacpp` |
|
||||
| `any-llm` | при `ollama` или `llamacpp` |
|
||||
|
||||
Строки с несовпавшим `requires` пропускаются (лог), остальные ставятся как обычно. В `extensions.example.yaml` по умолчанию — **swarm-assistent** с `requires: ollama` (чат/vision под Krea 2).
|
||||
|
||||
В git репозитория gpu-rent не коммитить рабочий список с лишними приватными URL сверх примера.
|
||||
|
||||
## Когда в жизненном цикле
|
||||
|
||||
|
||||
+81
-38
@@ -1,79 +1,122 @@
|
||||
# LLM рядом со SwarmUI (opt-in)
|
||||
|
||||
По умолчанию поднимается только SwarmUI. Ollama или llama.cpp — по флагу, `LLM_RUNTIME` в `gpu-rent.vars` / `.env`, или через wizard.
|
||||
По умолчанию поднимается **только SwarmUI**. Ollama или llama.cpp — отдельно, когда нужны (помощь с промптами и т.п.).
|
||||
|
||||
## Включение
|
||||
---
|
||||
|
||||
## Быстрый путь
|
||||
|
||||
### Вариант A — wizard
|
||||
|
||||
```text
|
||||
gpu-rent setup # спросит none/ollama/llamacpp + пресет моделей
|
||||
gpu-rent up --ollama # разово
|
||||
gpu-rent up --llm llamacpp
|
||||
# или в gpu-rent.vars:
|
||||
gpu-rent setup
|
||||
```
|
||||
|
||||
Выбери `ollama` или `llamacpp`, при Ollama — пресет моделей. Значение пишется в `gpu-rent.vars` (`LLM_RUNTIME=…`).
|
||||
|
||||
### Вариант B — флаг на `up`
|
||||
|
||||
```text
|
||||
gpu-rent up --yes --ollama
|
||||
gpu-rent up --yes --llm llamacpp
|
||||
```
|
||||
|
||||
### Вариант C — вручную в vars
|
||||
|
||||
```env
|
||||
LLM_RUNTIME=ollama
|
||||
```
|
||||
|
||||
Без параметров `gpu-rent` показывает help (не `up`). Double-click лаунчеры: `GPU_RENT_DEFAULT_ARGS=up --yes`. `gpu-rent up` без `--yes` спросит про LLM, если в vars ещё `none`. Полный doctor: `gpu-rent up -v`.
|
||||
Потом обычный `gpu-rent up --yes`.
|
||||
|
||||
## Порты (только loopback + туннель)
|
||||
Чтобы **выключить** LLM на следующих сессиях: `LLM_RUNTIME=none` (на `up` старые unit’ы `gpu-rent-ollama` / `gpu-rent-llamacpp` останавливаются).
|
||||
|
||||
| Сервис | VM | localhost |
|
||||
Голый `gpu-rent` без args — help, не `up`. Двойной клик: `GPU_RENT_DEFAULT_ARGS=up --yes` в `gpu-rent.vars`. Полный doctor на up: `up -v`.
|
||||
|
||||
---
|
||||
|
||||
## Порты (loopback + туннель)
|
||||
|
||||
| Сервис | На VM | На ноутбуке |
|
||||
| --- | --- | --- |
|
||||
| SwarmUI | 7801 | 17801 |
|
||||
| Ollama | 11434 | 17811 |
|
||||
| llama.cpp | 8080 | 17812 |
|
||||
| SwarmUI | 7801 | **17801** |
|
||||
| Ollama | 11434 | **17811** |
|
||||
| llama.cpp | 8080 | **17812** |
|
||||
|
||||
```text
|
||||
gpu-rent tunnel
|
||||
gpu-rent open --llm # http://127.0.0.1:17811 (Ollama)
|
||||
# клиент:
|
||||
set OLLAMA_HOST=http://127.0.0.1:17811
|
||||
gpu-rent open --llm
|
||||
```
|
||||
|
||||
## Ollama models
|
||||
Клиент Ollama:
|
||||
|
||||
Как Civitai `models.yaml`:
|
||||
```text
|
||||
# Windows PowerShell
|
||||
$env:OLLAMA_HOST = "http://127.0.0.1:17811"
|
||||
```
|
||||
|
||||
- `ollama-models.example.yaml` — в git
|
||||
- `ollama-models.yaml` — локальный (gitignore)
|
||||
После успешного `up`/`tunnel` CLI печатает access-card со всеми URL и MCP-сниппетом.
|
||||
|
||||
На `up` при `LLM_RUNTIME=ollama` CLI делает `ollama pull` по списку. Уже скачанные не трогает; лишние на диске не удаляет.
|
||||
---
|
||||
|
||||
### Пресеты setup
|
||||
## Ollama: модели
|
||||
|
||||
| Файл | Роль |
|
||||
| --- | --- |
|
||||
| `ollama-models.example.yaml` | шаблон в git |
|
||||
| `ollama-models.yaml` | твой список (gitignore) |
|
||||
|
||||
На `up` при `LLM_RUNTIME=ollama` CLI делает `ollama pull` по списку. Уже скачанные теги **точно** не трогает (`foo:3b` ≠ `foo:7b`). Лишние модели на диске не удаляет.
|
||||
|
||||
### Пресеты `setup`
|
||||
|
||||
| preset | tag | зачем |
|
||||
| --- | --- | --- |
|
||||
| **recommended** | `huihui_ai/qwen2.5-abliterate:7b` | RU/EN, ~5GB, мало отказов — помощь с промптами |
|
||||
| **recommended** | `huihui_ai/qwen2.5-abliterate:7b` | RU/EN, ~5 GB, меньше отказов |
|
||||
| light | `qwen2.5:3b` | быстрее, слабее |
|
||||
| stock | `qwen2.5:7b` | официальный, больше цензуры |
|
||||
| stock | `qwen2.5:7b` | официальный |
|
||||
| alt | `richardyoung/qwen2.5-7b-instruct-abliterated` | другой abliterate |
|
||||
| empty | `[]` | только runtime |
|
||||
| empty | `[]` | только runtime, pull руками |
|
||||
|
||||
Community abliterate-модели без гарантий безопасности — для личного prompt-help / NSFW-тегов рядом со SwarmUI.
|
||||
Community abliterate-модели без гарантий безопасности — для личного prompt-help рядом со SwarmUI.
|
||||
|
||||
Поле `default: true` в yaml — preferred модель (лог / подсказка); pull идёт по всему списку.
|
||||
|
||||
---
|
||||
|
||||
## llama.cpp
|
||||
|
||||
Ставит `llama-server` и systemd. GGUF клади вручную в `/mnt/swarm_data/llamacpp/models` на data-диске (или через SSH), затем `systemctl restart gpu-rent-llamacpp`.
|
||||
1. `LLM_RUNTIME=llamacpp` или `up --llamacpp`
|
||||
2. CLI ставит `llama-server` + systemd `gpu-rent-llamacpp`
|
||||
3. Положи GGUF в `/mnt/swarm_data/llamacpp/models` (SFTP / `gpu-rent ssh`)
|
||||
4. `systemctl restart gpu-rent-llamacpp` на VM
|
||||
|
||||
## Idle-killer
|
||||
Без GGUF unit может стартовать, но API бесполезен — смотри `gpu-rent logs` / `journalctl -u gpu-rent-llamacpp`.
|
||||
|
||||
Busy также если идёт `ollama pull` (маркер младше ~45 мин), в Ollama есть loaded model, или llama.cpp занимает слоты. Зависший маркер `.gpu-rent-ollama-pulling` старше 45 мин idle-killer сбрасывает.
|
||||
---
|
||||
|
||||
## Supply-chain (install на VM)
|
||||
## Idle-killer и LLM
|
||||
|
||||
Скрипты `install_ollama.sh` / `install_llamacpp.sh` по умолчанию тянут upstream **без pin** (Ollama: `curl|sh`; llama.cpp: GitHub `latest`). Это риск подмены артефакта.
|
||||
Busy (не гасить GPU), если:
|
||||
|
||||
Рекомендуется задать pin через env при bootstrap (или патч vars / future hooks):
|
||||
- идёт `ollama pull` (маркер младше ~45 мин; старше — сбрасывается);
|
||||
- в Ollama есть загруженная модель;
|
||||
- llama.cpp занимает слоты.
|
||||
|
||||
Ошибка установки LLM на `up` — **fail** (не тихий лог): почини или поставь `LLM_RUNTIME=none`.
|
||||
|
||||
---
|
||||
|
||||
## Supply-chain (опциональный pin)
|
||||
|
||||
По умолчанию install-скрипты тянут upstream **без pin** (Ollama: `curl|sh`; llama.cpp: GitHub `latest`) — риск подмены. В логе будет `WARN`.
|
||||
|
||||
Жёстче — задай env на VM / при bootstrap:
|
||||
|
||||
```bash
|
||||
# Ollama — GitHub release + checksum
|
||||
OLLAMA_VERSION=0.6.5
|
||||
OLLAMA_SHA256=<sha256 of ollama-linux-amd64.tgz>
|
||||
|
||||
# llama.cpp — tag или прямой URL + checksum
|
||||
LLAMACPP_TAG=b4690
|
||||
# или:
|
||||
LLAMACPP_ASSET_URL=https://github.com/ggerganov/llama.cpp/releases/download/...
|
||||
LLAMACPP_SHA256=<sha256 of archive>
|
||||
# или LLAMACPP_ASSET_URL=... + LLAMACPP_SHA256=...
|
||||
```
|
||||
|
||||
Без этих переменных в логе будет `WARN` про отсутствие pin/checksum.
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
# Локальные папки приложения
|
||||
|
||||
Все живут в корне gpu-rent. Одно правило: **пусто → ничего не синхронизируем**. Появился файл → на `up` (и по отдельной команде) едет инкремент по SHA256. С сервера лишнее не удаляем.
|
||||
Все живут в корне gpu-rent.
|
||||
|
||||
**Одно правило:** пусто (только README / `.gitkeep`) → ничего не синхронизируем. Появился файл → на `up` (или отдельной командой) едет инкремент по SHA256. С сервера лишнее **не** удаляем.
|
||||
|
||||
```powershell
|
||||
# положил LoRA / wildcard / workflow локально
|
||||
.\gpu-rent.ps1 push # или просто следующий up
|
||||
.\gpu-rent.ps1 pull-output # картинки с VM → ./Output
|
||||
```
|
||||
|
||||
| Локально | На VM | Направление | Обязательно |
|
||||
| --- | --- | --- | --- |
|
||||
|
||||
+14
-3
@@ -2,10 +2,21 @@
|
||||
|
||||
Два источника на data volume, они **складываются**, не заменяют друг друга:
|
||||
|
||||
1. **Civitai-seed** (ниже) — один раз на пустой диск, если есть API-токен и манифест.
|
||||
2. **Папка `Models/` в корне gpu-rent** — на каждом `up` выгрузить на VM только то, что изменилось. `Wildcards/` и `CustomWorkflows/` — то же правило, см. [local-folders.md](local-folders.md).
|
||||
1. **Civitai-seed** (ниже) — на пустой диск при bootstrap, если есть API-токен и манифест.
|
||||
2. **Папка `Models/` в корне gpu-rent** — на каждом `up` выгрузить только изменившееся. `Wildcards/` и `CustomWorkflows/` — то же, см. [local-folders.md](local-folders.md).
|
||||
|
||||
Локальный SwarmUI на `7801` по-прежнему не зеркалируем. Только дерево `./Models` приложения.
|
||||
## Быстрый старт моделей
|
||||
|
||||
```powershell
|
||||
copy models.example.yaml models.yaml
|
||||
# правь URL с modelVersionId=…
|
||||
# в .env: CIVITAI_API_TOKEN=…
|
||||
.\gpu-rent.ps1 up --yes
|
||||
```
|
||||
|
||||
Или положи веса в `Models/Stable-Diffusion/` (и т.п.) — уедут на `up` / `gpu-rent push-models`.
|
||||
|
||||
Локальный SwarmUI на `7801` не зеркалируем. Только дерево `./Models` приложения.
|
||||
|
||||
## Локальная папка → сервер
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Открытые вопросы
|
||||
|
||||
Пока пусто по продуктовым развилкам — см. [decisions.md](decisions.md).
|
||||
Продуктовые развилки закрыты — см. [decisions.md](decisions.md).
|
||||
|
||||
Осталось подтвердить на **spike**, не в споре:
|
||||
Ниже — что **подтвердить на spike** (живой GPU), не в споре:
|
||||
|
||||
- Качалка модели в UI: отдельного poll-API нет (`DoModelDownloadWS` только WS) → v1 считает busy через `waiting_gens` / `live_gens` / `loading_models` / backend≠idle; иначе пользователь жмёт `hold`.
|
||||
- Реальная цена 100 GB сетевого диска в выбранном сегменте и цена 1×4090 preemptible ₽/час (в OpenStack API нет).
|
||||
- Имя GPU-образа (без Docker) и flavor id в твоём пуле (в git не класть); какие из списка фоллбека реально есть.
|
||||
- Хватает ли application credential с access_rules (delete/GET server) на Selectel — при отказе CLI падает назад на unrestricted cred и пишет в лог.
|
||||
- Качалка модели в UI: отдельного poll-API нет → v1 считает busy через очередь / loading / backend≠idle; иначе пользователь жмёт `hold`.
|
||||
- Реальная цена 100 GB сетевого диска и 1×4090 preemptible ₽/час в твоём сегменте (в OpenStack API нет — только панель).
|
||||
- Имя GPU-образа (без Docker) и flavor id в твоём пуле (в git не класть); какие из `FLAVOR_PREFERENCE` реально есть.
|
||||
- Application credential с **узкими** access_rules (DELETE/GET только этого `server_id`) на Selectel: если Keystone отвергает rules — CLI **fail closed** (idle-killer не вооружается unrestricted). Нужно проверить на spike и при необходимости править права / путь.
|
||||
- Snapshot attached boot volume после Idle: время и можно ли сразу create from snapshot.
|
||||
|
||||
Если spike покажет, что 45 минут льготы мало на первую установку ComfyUI — поднять `IDLE_GRACE_MINUTES`, не отключать killer.
|
||||
Если spike покажет, что 45 минут льготы мало на первую установку ComfyUI — подними `IDLE_GRACE_MINUTES`, не отключай killer.
|
||||
|
||||
+7
-6
@@ -1,16 +1,17 @@
|
||||
# Roadmap
|
||||
|
||||
**Код v1 (§1–6) готов.** Дальше блокирует только облако: **0.0 квота GPU** и **0 spike** в панели. Без GPU в квоте `up` не создать — это ожидаемо.
|
||||
**Код v1 (§1–6) готов** (включая opt-in LLM, local-watchdog, access-card). Дальше — твой облачный прогон.
|
||||
|
||||
Чеклист прогона: [spike-notes.md](spike-notes.md). Ключи: [setup.md](setup.md).
|
||||
Пользовательский путь: [setup.md](setup.md) → `doctor` → `up`. Чеклист живого GPU: [spike-notes.md](spike-notes.md).
|
||||
|
||||
## 0.0 Квота GPU
|
||||
|
||||
- [x] Квота GPU в аккаунте в порядке (подтверждено)
|
||||
- [ ] Тикет в поддержку Selectel — не нужен, если лимит уже > 0
|
||||
- [ ] Сервисный пользователь с правом `member` на проект + `.env` заполнен
|
||||
- [ ] Квота GPU в панели ≥ 1 (иначе тикет — текст в setup §2.3)
|
||||
- [ ] Тикет закрыт / не нужен
|
||||
- [ ] Сервисный пользователь `member` + `.env` заполнен
|
||||
- [ ] `gpu-rent doctor` exit 0
|
||||
|
||||
`doctor` / `flavors` / `dry-run` — дальше; `up` после зелёного doctor.
|
||||
`flavors` / `dry-run` — по желанию перед первым `up`.
|
||||
|
||||
## 0. Ручной spike в панели
|
||||
|
||||
|
||||
+158
-104
@@ -1,20 +1,38 @@
|
||||
# Что сделать до первого `gpu-rent up`
|
||||
# Подготовка до первого `up`
|
||||
|
||||
Код **не создаёт GPU**, пока не пройден `gpu-rent doctor`. Сначала доступ в облако, квота GPU и ключи. Статический ключ панели **`X-Token` для этого CLI не подходит** — им нельзя управлять серверами и дисками.
|
||||
Цель: зелёный `doctor` → первый `up --yes` → UI на `http://127.0.0.1:17801` → `stop`.
|
||||
|
||||
Панель: [my.selectel.ru](https://my.selectel.ru).
|
||||
Панель Selectel: [my.selectel.ru](https://my.selectel.ru).
|
||||
|
||||
**Важно:** ключ панели **`X-Token` не подходит**. Нужен сервисный пользователь + пароль (OpenStack Keystone). CLI сам получает токен на ~24 ч; в `.env` токен хранить не надо.
|
||||
|
||||
---
|
||||
|
||||
## 1. Python
|
||||
## Карта шагов
|
||||
|
||||
| # | Что сделать | Готово, когда |
|
||||
| --- | --- | --- |
|
||||
| 1 | Python 3.11+ и лаунчер | `.\gpu-rent.ps1 --help` печатает help |
|
||||
| 2 | Проект + квота GPU > 0 | в панели лимит GPU ≥ 1 |
|
||||
| 3 | Сервисный пользователь + `.env` | заполнены `OS_*` и `GPU_RENT_AZ` |
|
||||
| 4 | (Опц.) Civitai + `models.yaml` | токен в `.env`, манифест с `modelVersionId` |
|
||||
| 5 | (Опц.) `extensions.yaml` / Git | публичные репы без токена |
|
||||
| 6 | `doctor` | exit 0 |
|
||||
| 7 | Первый `up --yes` | браузер / :17801 |
|
||||
| 8 | `stop` | compute удалён, диски на месте |
|
||||
|
||||
SSH-ключ руками не нужен — CLI создаст `.gpu-rent/id_ed25519` при первом `up`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Python и лаунчер
|
||||
|
||||
Нужен **Python 3.11+**. На Windows при установке включи «Add python.exe to PATH».
|
||||
|
||||
В корне репозитория достаточно лаунчера — venv и `pip install` он сделает сам:
|
||||
В корне репозитория:
|
||||
|
||||
```powershell
|
||||
.\gpu-rent.ps1 --help
|
||||
.\gpu-rent.ps1 doctor
|
||||
```
|
||||
|
||||
```bat
|
||||
@@ -26,11 +44,24 @@ chmod +x gpu-rent.sh
|
||||
./gpu-rent.sh --help
|
||||
```
|
||||
|
||||
Первый запуск копирует `env.example` → `.env`, `models.example.yaml` → `models.yaml`, `extensions.example.yaml` → `extensions.yaml` в **корне репозитория**, если файлов ещё нет. Заполни `OS_*` в `.env` (шаги ниже). После `git pull`, если изменился `pyproject.toml`, лаунчер переустановит пакет.
|
||||
Лаунчер сам создаёт `.venv` и ставит пакет. Первый запуск копирует шаблоны **в корень репо**, если файлов нет:
|
||||
|
||||
Секреты и runtime не уезжают в `%USERPROFILE%`: только `<repo>/.env` и `<repo>/.gpu-rent/` (state, SSH-ключ). Оба в `.gitignore`. Если раньше лежало в `~\.gpu-rent\`, CLI один раз перенесёт в проект.
|
||||
- `env.example` → `.env`
|
||||
- `models.example.yaml` → `models.yaml`
|
||||
- `extensions.example.yaml` → `extensions.yaml`
|
||||
- `gpu-rent.vars.example` → `gpu-rent.vars`
|
||||
|
||||
Ручной венв по желанию (для разработки / pytest):
|
||||
Секреты только в `<repo>/.env` и runtime в `<repo>/.gpu-rent/` (оба в `.gitignore`).
|
||||
|
||||
Опционально wizard (после того как `.env` хотя бы частично заполнен):
|
||||
|
||||
```powershell
|
||||
.\gpu-rent.ps1 setup
|
||||
```
|
||||
|
||||
Спросит LLM (none/ollama/llamacpp) и local-watchdog. Можно пропустить и настроить позже.
|
||||
|
||||
Для разработки / pytest:
|
||||
|
||||
```powershell
|
||||
python -m venv .venv
|
||||
@@ -44,171 +75,194 @@ python -m gpu_rent --help
|
||||
|
||||
## 2. Selectel: проект и квота GPU
|
||||
|
||||
На новых аккаунтах лимит GPU почти всегда **0**. Без тикета в поддержку `up` создать карту не сможет — это норма, не баг CLI.
|
||||
На новых аккаунтах лимит GPU часто **0**. Без поднятия лимита `up` не создаст сервер — это нормально, не баг CLI.
|
||||
|
||||
### 2.1. Проект
|
||||
|
||||
1. Панель → сверху **IAM** → **Projects** (Проекты).
|
||||
2. Отдельный проект, например `gpu-rent`. Не клади GPU в общий «мусорный» проект.
|
||||
3. Скопируй **ID проекта** (uuid). Он же `OS_PROJECT_ID`.
|
||||
4. **Номер аккаунта** — в правом верхнем углу панели. Он же `OS_USER_DOMAIN_NAME` (domain в Keystone).
|
||||
1. Панель → **IAM** → **Projects** (Проекты).
|
||||
2. Отдельный проект, например `gpu-rent` (не общий «мусорный»).
|
||||
3. Скопируй **ID проекта** (uuid) → это `OS_PROJECT_ID`.
|
||||
4. **Номер аккаунта** (правый верхний угол) → это `OS_USER_DOMAIN_NAME`.
|
||||
|
||||
### 2.2. Есть ли GPU в квоте
|
||||
|
||||
1. **IAM** → **Projects** → твой проект → вкладка **Quotas and limits** / **Квоты и лимиты** → **Cloud platform**.
|
||||
2. Выбери пул и сегмент, где есть GPU (матрица: [GPU availability](https://docs.selectel.ru/en/cloud-servers/create/gpus/)). Для Москвы смотри **мультизональный `ru-6`** (`ru-6a`/`ru-6b`/`ru-6c`) и `ru-7`. CLI сам сканит `SCAN_POOLS` в `gpu-rent flavors` и подскажет `OS_REGION_NAME` / `GPU_RENT_AZ`.
|
||||
3. Строка **GPU** (и при необходимости vCPU/RAM/network volumes). Если лимит 0 — дальше тикет.
|
||||
1. **IAM** → **Projects** → твой проект → **Quotas and limits** / **Квоты** → **Cloud platform**.
|
||||
2. Пул и сегмент с GPU: матрица [GPU availability](https://docs.selectel.ru/en/cloud-servers/create/gpus/). Часто смотрят мультизональный `ru-6` и `ru-7`.
|
||||
3. Строка **GPU**. Если лимит 0 — тикет (ниже).
|
||||
|
||||
Квоту внутри уже выданного лимита можно крутить в панели. **Сам лимит GPU поднимает только поддержка.**
|
||||
Лимит GPU поднимает **только поддержка**. Внутри уже выданного лимита квоту можно крутить в панели.
|
||||
|
||||
Подсказка по пулу после появления ключей: `.\gpu-rent.ps1 flavors` (скан `SCAN_POOLS`).
|
||||
|
||||
### 2.3. Тикет в поддержку
|
||||
|
||||
**Тикеты** в панели (не email вслепую). Лимит увеличивают **на один конкретный проект**.
|
||||
|
||||
Текст можно почти копировать:
|
||||
**Тикеты** в панели. Лимит увеличивают **на один конкретный проект**.
|
||||
|
||||
```text
|
||||
Прошу увеличить лимит GPU в облачной платформе.
|
||||
|
||||
Проект: <имя> (ID: <uuid проекта>)
|
||||
Пул / сегмент: ru-7 / ru-7a ← подставь свой из матрицы GPU
|
||||
Нужно: 1× NVIDIA RTX 4090 24 GB (если нет — ближайший аналог в этом сегменте: 4090 48 GB или A5000).
|
||||
Цель: один прерываемый (preemptible) облачный сервер для персональных сессий генерации, диски сетевые.
|
||||
Нужно: 1× NVIDIA RTX 4090 24 GB (если нет — ближайший аналог: 4090 48 GB или A5000).
|
||||
Цель: один прерываемый (preemptible) облачный сервер для персональных сессий, диски сетевые.
|
||||
|
||||
Сейчас квота/лимит GPU = 0, создать сервер с GPU нельзя.
|
||||
```
|
||||
|
||||
Пока тикет не закрыт, ставишь CLI и гоняешь `doctor` — он как раз покажет «квота 0, напишите в поддержку».
|
||||
Пока тикет открыт, можно уже заполнять `.env` и гонять `doctor` — он покажет «квота 0».
|
||||
|
||||
---
|
||||
|
||||
## 3. Ключи OpenStack (это и есть «API key» для gpu-rent)
|
||||
## 3. Ключи OpenStack
|
||||
|
||||
Нужен **сервисный пользователь** + пароль. CLI сам получает IAM-токен на 24 часа (`X-Auth-Token`). В `.env` токен хранить не надо.
|
||||
|
||||
**Не используй:** Профиль → Access → API Keys → `X-Token`. Это статический ключ панели, OpenStack (серверы/диски/сети) он **не** двигает.
|
||||
Нужен **сервисный пользователь** + пароль. Не используй: Профиль → Access → API Keys → `X-Token`.
|
||||
|
||||
### 3.1. Сервисный пользователь
|
||||
|
||||
Только владелец аккаунта или роль `iam.admin`. На балансе для роли `member` должно быть хотя бы **100 ₽**.
|
||||
Только владелец аккаунта или `iam.admin`. На балансе для роли `member` обычно нужно хотя бы **~100 ₽**.
|
||||
|
||||
1. Сверху **IAM** → **Service users** / **Сервисные пользователи**.
|
||||
2. **Add service user**.
|
||||
3. Имя, например `gpu-rent-api`.
|
||||
4. Пароль: **минимум 20 символов**, сохрани в менеджер паролей. После создания пароль **больше не показывают** — только сброс.
|
||||
5. Права:
|
||||
- **Scope: Projects** (не весь аккаунт);
|
||||
- проект `gpu-rent`;
|
||||
- роль **`member`** (создание серверов/дисков/сетей). Роль `reader` для `up` не хватит.
|
||||
6. **Add user**.
|
||||
1. **IAM** → **Service users** / **Сервисные пользователи** → **Add**.
|
||||
2. Имя, например `gpu-rent-api`.
|
||||
3. Пароль: **≥ 20 символов**, сохрани сразу — потом только сброс.
|
||||
4. Scope: **Projects** → проект `gpu-rent` → роль **`member`** (`reader` для `up` мало).
|
||||
5. **Add user**.
|
||||
|
||||
Официально: [Add user](https://docs.selectel.ru/en/access-control/manage/add-user/), [авторизация API](https://docs.selectel.ru/en/api/authorization/).
|
||||
Официально: [Add user](https://docs.selectel.ru/en/access-control/manage/add-user/), [API auth](https://docs.selectel.ru/en/api/authorization/).
|
||||
|
||||
### 3.2. Скачать RC-файл (готовые `OS_*`)
|
||||
### 3.2. RC-файл
|
||||
|
||||
1. **IAM** → **Service users** → твой пользователь → вкладка **Access**.
|
||||
2. Блок **RC files**:
|
||||
- проект `gpu-rent`;
|
||||
- локация = **пул**, например `ru-7` (это `OS_REGION_NAME`, не сегмент `ru-7a`);
|
||||
- **Download**.
|
||||
3. Открой файл (`rc.sh`). Из него в `.env` переносятся:
|
||||
1. **IAM** → **Service users** → пользователь → **Access**.
|
||||
2. **RC files**: проект `gpu-rent`, локация = **пул** (например `ru-7`, не сегмент `ru-7a`) → **Download**.
|
||||
3. Из `rc.sh` перенеси в `.env`:
|
||||
|
||||
| Переменная | Откуда |
|
||||
| --- | --- |
|
||||
| `OS_AUTH_URL` | обычно `https://cloud.api.selcloud.ru/identity/v3` |
|
||||
| `OS_USER_DOMAIN_NAME` | номер аккаунта |
|
||||
| `OS_PROJECT_DOMAIN_NAME` | тот же номер (можно не дублировать в нашем `.env`) |
|
||||
| `OS_PROJECT_ID` | uuid проекта |
|
||||
| `OS_USERNAME` | имя сервисного пользователя |
|
||||
| `OS_PASSWORD` | пароль, который ты сохранил (в RC его часто нет — дописываешь сам) |
|
||||
| `OS_REGION_NAME` | пул, `ru-7` |
|
||||
| `GPU_RENT_AZ` | **сегмент** пула, `ru-7a` — в RC его может не быть, смотри матрицу GPU |
|
||||
| `OS_PASSWORD` | пароль (в RC часто нет — допиши сам) |
|
||||
| `OS_REGION_NAME` | пул, например `ru-7` |
|
||||
| `GPU_RENT_AZ` | **сегмент**, например `ru-7a` (в RC может не быть) |
|
||||
|
||||
Официально: [Configure OpenStack CLI](https://docs.selectel.ru/en/cloud-servers/tools/openstack-cli/configure-openstack-cli/).
|
||||
|
||||
### 3.3. Куда класть
|
||||
|
||||
Создай каталог и файл **вне git**:
|
||||
### 3.3. Заполнить `.env`
|
||||
|
||||
```powershell
|
||||
copy env.example .env
|
||||
notepad .env
|
||||
```
|
||||
|
||||
Вставь значения из RC + пароль + `GPU_RENT_AZ`. Никогда не коммить `.env`.
|
||||
Вставь значения из RC + пароль + `GPU_RENT_AZ`. **Не коммить** `.env`.
|
||||
|
||||
Проверка без нашего CLI (необязательно):
|
||||
|
||||
```powershell
|
||||
# после pip install python-openstackclient, если хочешь
|
||||
openstack token issue
|
||||
openstack flavor list
|
||||
```
|
||||
|
||||
Наш способ: `gpu-rent doctor`.
|
||||
Проверка: `.\gpu-rent.ps1 doctor` (не обязательно ставить `openstack` CLI).
|
||||
|
||||
---
|
||||
|
||||
## 4. Civitai API token (модели)
|
||||
|
||||
Нужен, если хочешь seed с Civitai по `models.yaml`. Без токена SwarmUI поставит свою дефолтную модель — это допустимо.
|
||||
Нужен, если хочешь seed по `models.yaml`. Без токена SwarmUI поставит свою дефолтную модель — это нормально.
|
||||
|
||||
1. Войди на [civitai.com](https://civitai.com) (тот же аккаунт, что и для `.red`).
|
||||
2. [Account settings](https://civitai.com/user/account) → блок **API Keys** → **Add API key**.
|
||||
3. Имя, например `gpu-rent`. Токен показывают **один раз**.
|
||||
4. В `.env`: `CIVITAI_API_TOKEN=...`
|
||||
5. Хост API по умолчанию **`civitai.red`** (полный каталог). `.com` — SFW-витрина, NSFW с неё часто 404. Один токен на оба домена.
|
||||
|
||||
Не клади токен в query-string в скриптах «на память» — в логах светится. CLI шлёт `Authorization: Bearer …` только на `civitai.com` / `civitai.red` / `civitai.green`, не на CDN.
|
||||
|
||||
Манифест (не в git со своими id, если не хочешь светить вкусы):
|
||||
1. Войди на [civitai.com](https://civitai.com) (тот же аккаунт, что для `.red`).
|
||||
2. [Account settings](https://civitai.com/user/account) → **API Keys** → **Add**.
|
||||
3. Токен показывают **один раз** → в `.env`: `CIVITAI_API_TOKEN=...`
|
||||
4. Хост по умолчанию **`civitai.red`** (полный каталог). С `.com` NSFW часто 404.
|
||||
5. Манифест:
|
||||
|
||||
```powershell
|
||||
copy models.example.yaml models.yaml
|
||||
```
|
||||
|
||||
`version_id: 0` — заглушка, doctor её игнорирует. Нужен **modelVersionId** из URL, не id карточки модели.
|
||||
В URL нужен **`modelVersionId=`**, не id карточки модели. Подробности: [models.md](models.md).
|
||||
|
||||
---
|
||||
|
||||
## 5. Git-токен (только приватные репы расширений)
|
||||
## 5. Git-токен (только приватные репы)
|
||||
|
||||
Публичные GitHub-репы в `extensions.yaml` клонируются без токена.
|
||||
Публичные репы в `extensions.yaml` клонируются без токена.
|
||||
|
||||
Если репа приватная:
|
||||
|
||||
1. GitHub → Settings → Developer settings → Personal access tokens.
|
||||
2. Fine-grained: доступ только к нужным репам, **Contents: Read**.
|
||||
3. `GIT_TOKEN` в `.env`.
|
||||
|
||||
Скопируй шаблон: `extensions.example.yaml` → `extensions.yaml` в корне репо. Пустой файл = стоковый SwarmUI.
|
||||
Приватные: GitHub fine-grained PAT, **Contents: Read** → `GIT_TOKEN` в `.env`.
|
||||
Шаблон: `extensions.example.yaml` → `extensions.yaml`. Пустой файл = стоковый SwarmUI. См. [extensions.md](extensions.md).
|
||||
|
||||
---
|
||||
|
||||
## 6. SSH
|
||||
## 6. Чеклист перед `doctor`
|
||||
|
||||
Ключ **не надо** делать руками. CLI создаст `<repo>\.gpu-rent\id_ed25519` без passphrase и зарегистрирует keypair в OpenStack при первом `up`. `doctor` только проверяет, что это получится.
|
||||
|
||||
---
|
||||
|
||||
## 7. Чеклист перед `doctor`
|
||||
|
||||
- [ ] Python 3.11+, `pip install -e .`
|
||||
- [ ] Проект Selectel, скопирован uuid
|
||||
- [ ] Тикет на лимит **1× GPU** в нужном сегменте (или квота уже > 0)
|
||||
- [ ] Сервисный пользователь `member` на этот проект, пароль сохранён
|
||||
- [ ] RC скачан на **тот же пул**, где GPU
|
||||
- [ ] `.env` в корне репо заполнен (`OS_*` + `GPU_RENT_AZ`)
|
||||
- [ ] Нет `X-Token` вместо пароля сервисного пользователя
|
||||
- [ ] (опционально) Civitai token + `models.yaml`
|
||||
- [ ] На балансе хватает на диск 100 GB **даже когда GPU выключен**
|
||||
|
||||
Дальше:
|
||||
- [ ] Python 3.11+, лаунчер отвечает на `--help`
|
||||
- [ ] Проект Selectel, uuid скопирован
|
||||
- [ ] Лимит GPU ≥ 1 (или тикет в работе — тогда `doctor` честно скажет «0»)
|
||||
- [ ] Сервисный пользователь `member`, пароль сохранён
|
||||
- [ ] RC с того же **пула**, где GPU
|
||||
- [ ] `.env`: `OS_*` + `GPU_RENT_AZ`, не `X-Token`
|
||||
- [ ] (опц.) Civitai + `models.yaml`
|
||||
- [ ] На балансе хватает на **диск ~100 GB 24/7**, даже когда GPU выключен
|
||||
|
||||
```powershell
|
||||
gpu-rent doctor
|
||||
.\gpu-rent.ps1 doctor
|
||||
.\gpu-rent.ps1 flavors
|
||||
.\gpu-rent.ps1 dry-run
|
||||
```
|
||||
|
||||
Exit 0 — облако отвечает, можно идти к spike / `up`, когда команда появится. Exit 1 — в отчёте причина (часто квота GPU = 0).
|
||||
| Результат | Что делать |
|
||||
| --- | --- |
|
||||
| exit 0 | можно `up` |
|
||||
| exit 1 | читай отчёт: чаще всего квота GPU = 0 или неверный пароль / пул |
|
||||
|
||||
`gpu-rent dry-run` печатает план без создания сервера.
|
||||
---
|
||||
|
||||
## 7. Первый `up`
|
||||
|
||||
Первый прогон долгий (образ, SwarmUI, Comfy, seed): ориентир **20–40 минут**. Не закрывай терминал посередине bootstrap.
|
||||
|
||||
```powershell
|
||||
.\gpu-rent.ps1 up --yes
|
||||
```
|
||||
|
||||
Что произойдёт:
|
||||
|
||||
1. Снова короткий doctor (кратко; полный — `up -v`).
|
||||
2. Create/unshelve preemptible GPU + диски.
|
||||
3. Bootstrap SwarmUI, extensions, autocomplete, Civitai-seed, push локальных папок.
|
||||
4. Туннель на `localhost:17801`, access-card с URL / MCP.
|
||||
5. Процесс ждёт: **Ctrl+C** закрывает только туннель, GPU остаётся.
|
||||
|
||||
Полезные флаги:
|
||||
|
||||
| Флаг | Зачем |
|
||||
| --- | --- |
|
||||
| `--no-tunnel` | только облако; UI потом: `tunnel --open` |
|
||||
| `--no-update` | не `git pull` SwarmUI/extensions |
|
||||
| `--ollama` | поднять Ollama рядом ([llm.md](llm.md)) |
|
||||
| `--no-spot` | обычный (не preemptible) тариф |
|
||||
| `--flavor ID` | явный flavor, без фоллбека |
|
||||
|
||||
Двойной клик без аргументов: в `gpu-rent.vars` задай `GPU_RENT_DEFAULT_ARGS=up --yes`. Сам `gpu-rent` без args показывает **help**, не поднимает GPU.
|
||||
|
||||
---
|
||||
|
||||
## 8. Закончить сессию
|
||||
|
||||
```powershell
|
||||
.\gpu-rent.ps1 stop
|
||||
```
|
||||
|
||||
Удаляет compute (+ FIP по умолчанию), **диски оставляет**. Модели на месте для следующего `up`.
|
||||
|
||||
| Команда | Эффект |
|
||||
| --- | --- |
|
||||
| `hold` / `hold --minutes 90` | отложить idle-killer |
|
||||
| `status` | Nova, диск, killer, LLM |
|
||||
| `destroy --i-understand-data-loss` | stop + удалить диски |
|
||||
|
||||
---
|
||||
|
||||
## Деньги и риски (кратко)
|
||||
|
||||
- **GPU** — пока жив compute.
|
||||
- **Data-диск** — тарифицируется **всегда** после создания.
|
||||
- Preemptible ~**24 ч** → `EXPIRED`; `tunnel` или `up` восстановят.
|
||||
- Idle-killer: после льготы (~45 мин) + ~30 мин пустой очереди → delete compute.
|
||||
- Цены в OpenStack API нет — смотри панель Selectel.
|
||||
|
||||
Дальше: чеклист живого прогона [spike-notes.md](spike-notes.md), справочник команд [cli.md](cli.md).
|
||||
|
||||
+63
-26
@@ -1,21 +1,27 @@
|
||||
# Spike notes (заполнять руками)
|
||||
# Spike notes — первый живой прогон
|
||||
|
||||
Без секретов, без личных имён файлов моделей. Цель — один раз пройти панель + CLI на живом GPU и зафиксировать факты для v1.
|
||||
Заполняй **руками** после зелёного `doctor`. Без секретов и без личных имён чекпоинтов.
|
||||
|
||||
## 0.0 Квота
|
||||
Подготовка ключей: [setup.md](setup.md). Цель — один раз пройти панель + CLI на реальном GPU и зафиксировать факты (времена, цены, имя образа).
|
||||
|
||||
---
|
||||
|
||||
## 0. Перед прогоном
|
||||
|
||||
| Шаг | Дата | Результат |
|
||||
| --- | --- | --- |
|
||||
| Квота GPU в панели (лимит) | | 0 / N |
|
||||
| Тикет в поддержку (номер) | | |
|
||||
| Лимит поднят | | |
|
||||
| Сервисный пользователь `member` на проект | | |
|
||||
| Тикет в поддержку (номер), если был | | |
|
||||
| Сервисный пользователь `member` + `.env` | | да / нет |
|
||||
| `gpu-rent doctor` exit 0 | | |
|
||||
|
||||
Текст тикета: [setup.md §2.3](setup.md).
|
||||
Текст тикета: [setup.md §2.3](setup.md#23-тикет-в-поддержку).
|
||||
|
||||
## Preflight CLI
|
||||
---
|
||||
|
||||
```text
|
||||
## 1. Preflight
|
||||
|
||||
```powershell
|
||||
.\gpu-rent.ps1 doctor
|
||||
.\gpu-rent.ps1 flavors
|
||||
.\gpu-rent.ps1 dry-run
|
||||
@@ -25,17 +31,21 @@
|
||||
| --- | --- | --- |
|
||||
| Keystone token | | |
|
||||
| GPU quota > 0 | | |
|
||||
| Flavor из FLAVOR_PREFERENCE | | id / имя |
|
||||
| Flavor из `FLAVOR_PREFERENCE` | | id / имя |
|
||||
| Image Driver 580 (без Docker) | | имя образа |
|
||||
| Volume type в AZ | | |
|
||||
| Civitai `.red` + token | | |
|
||||
| Civitai `.red` + token (если используешь) | | |
|
||||
|
||||
## Первый `up`
|
||||
---
|
||||
|
||||
```text
|
||||
## 2. Первый `up`
|
||||
|
||||
```powershell
|
||||
.\gpu-rent.ps1 up --yes
|
||||
```
|
||||
|
||||
Первый bootstrap долгий (ориентир 20–40 мин). Не рви терминал посередине.
|
||||
|
||||
| Метрика | Значение |
|
||||
| --- | --- |
|
||||
| Flavor фактически | |
|
||||
@@ -44,13 +54,18 @@
|
||||
| SSH через (мин) | |
|
||||
| Backend Idle через (мин) | |
|
||||
| Seed Civitai (мин / GB) | |
|
||||
| Boot snapshot создался | да / нет / ошибка |
|
||||
| Boot snapshot `gpu-rent-boot-ok` | да / нет / ошибка |
|
||||
| ₽/час GPU (панель) | |
|
||||
| ₽/мес data disk | |
|
||||
| ₽/мес data disk (панель) | |
|
||||
| Access-card / URL :17801 | да / нет |
|
||||
|
||||
## Туннель и API
|
||||
---
|
||||
|
||||
```text
|
||||
## 3. Туннель и API
|
||||
|
||||
Если закрыл туннель (Ctrl+C):
|
||||
|
||||
```powershell
|
||||
.\gpu-rent.ps1 tunnel --open
|
||||
```
|
||||
|
||||
@@ -59,31 +74,53 @@
|
||||
| UI http://127.0.0.1:17801 | |
|
||||
| `/API/GetNewSession` | |
|
||||
| MCP `/mcp` (если нужно) | |
|
||||
| `nvidia-smi` по `gpu-rent ssh` | |
|
||||
| `nvidia-smi` через `gpu-rent ssh` | |
|
||||
|
||||
## Idle-killer / hold
|
||||
---
|
||||
|
||||
## 4. Idle-killer / hold
|
||||
|
||||
| Шаг | OK? | Заметка |
|
||||
| --- | --- | --- |
|
||||
| `systemctl status gpu-rent-idle-killer.timer` | | |
|
||||
| `systemctl status gpu-rent-idle-killer.timer` (по SSH) | | |
|
||||
| `gpu-rent hold` пишет hold-until | | |
|
||||
| Качалка модели в UI → нужен hold? | | нет сигнала API — только hold |
|
||||
| Качалка модели в UI | | hold, если killer не видит busy |
|
||||
|
||||
## Preempt / диски
|
||||
---
|
||||
|
||||
## 5. Preempt / диски / stop
|
||||
|
||||
| Шаг | OK? |
|
||||
| --- | --- |
|
||||
| EXPIRED → tunnel unshelve или `up` | |
|
||||
| EXPIRED → `tunnel` unshelve или `up` | |
|
||||
| `stop` — диски живы | |
|
||||
| Второй `up` — те же модели на data | |
|
||||
| Boot из snapshot `gpu-rent-boot-ok` | |
|
||||
|
||||
## Выводы для кода
|
||||
---
|
||||
|
||||
## 6. (Опц.) LLM
|
||||
|
||||
```powershell
|
||||
.\gpu-rent.ps1 up --yes --ollama
|
||||
```
|
||||
|
||||
| Шаг | OK? |
|
||||
| --- | --- |
|
||||
| Ollama на :17811 через туннель | |
|
||||
| `ollama pull` из манифеста | |
|
||||
| `LLM_RUNTIME=none` гасит unit на следующем up | |
|
||||
|
||||
См. [llm.md](llm.md).
|
||||
|
||||
---
|
||||
|
||||
## Выводы для кода / конфига
|
||||
|
||||
- Имя GPU-образа в пуле:
|
||||
- Реальные flavor id (не в git):
|
||||
- Хватает ли `IDLE_GRACE_MINUTES=45`:
|
||||
- Application credential: ок / нужны другие права:
|
||||
- Application credential с узкими access_rules: ок / ошибка (fail closed):
|
||||
- Прочее:
|
||||
|
||||
Когда spike закрыт — отметь пункты в [roadmap.md](roadmap.md) §0 / §0.0.
|
||||
Когда spike закрыт — отметь пункты в [roadmap.md](roadmap.md) §0.
|
||||
|
||||
@@ -2,8 +2,14 @@
|
||||
# Empty/missing file → no extra extensions, stock SwarmUI.
|
||||
# swarmui = C# repos cloned to src/Extensions
|
||||
# comfy = Python custom nodes cloned to ComfyUI DLNodes
|
||||
# requires: none (default) | ollama | llamacpp | any-llm
|
||||
# — clone only when LLM_RUNTIME matches (ollama / llamacpp / either)
|
||||
|
||||
swarmui: []
|
||||
swarmui:
|
||||
- url: https://gitea.hsrv.site/mrleo1nid/swarm-assistent.git
|
||||
ref: main
|
||||
dir: swarm-assistent
|
||||
requires: ollama
|
||||
# - url: https://github.com/org/SwarmUI-MyExt.git
|
||||
# ref: main
|
||||
# dir: MyExt
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
+50
-1
@@ -1,6 +1,9 @@
|
||||
from pathlib import Path
|
||||
|
||||
from gpu_rent.manifests import parse_extensions, parse_models
|
||||
import pytest
|
||||
|
||||
from gpu_rent.errors import ConfigError
|
||||
from gpu_rent.manifests import parse_extensions, parse_models, repo_matches_runtime
|
||||
|
||||
|
||||
def test_models_skips_version_zero(tmp_path: Path):
|
||||
@@ -30,3 +33,49 @@ def test_extensions_repo(tmp_path: Path):
|
||||
repos = parse_extensions(path)
|
||||
assert repos[0].kind == "swarmui"
|
||||
assert repos[0].directory == "Ext"
|
||||
assert repos[0].requires == "none"
|
||||
|
||||
|
||||
def test_extensions_requires_ollama(tmp_path: Path):
|
||||
path = tmp_path / "extensions.yaml"
|
||||
path.write_text(
|
||||
"swarmui:\n"
|
||||
" - url: https://gitea.example/swarm-assistent.git\n"
|
||||
" ref: main\n"
|
||||
" dir: swarm-assistent\n"
|
||||
" requires: ollama\n"
|
||||
" - url: https://github.com/org/Always.git\n"
|
||||
" ref: main\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
repos = parse_extensions(path)
|
||||
assert repos[0].requires == "ollama"
|
||||
assert repos[1].requires == "none"
|
||||
assert repo_matches_runtime(repos[0], "ollama")
|
||||
assert not repo_matches_runtime(repos[0], "none")
|
||||
assert not repo_matches_runtime(repos[0], "llamacpp")
|
||||
assert repo_matches_runtime(repos[1], "none")
|
||||
assert repo_matches_runtime(repos[1], "ollama")
|
||||
|
||||
|
||||
def test_extensions_requires_any_llm(tmp_path: Path):
|
||||
path = tmp_path / "extensions.yaml"
|
||||
path.write_text(
|
||||
"swarmui:\n - url: https://example/x.git\n requires: any-llm\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
repo = parse_extensions(path)[0]
|
||||
assert repo.requires == "any-llm"
|
||||
assert repo_matches_runtime(repo, "ollama")
|
||||
assert repo_matches_runtime(repo, "llamacpp")
|
||||
assert not repo_matches_runtime(repo, "none")
|
||||
|
||||
|
||||
def test_extensions_requires_invalid(tmp_path: Path):
|
||||
path = tmp_path / "extensions.yaml"
|
||||
path.write_text(
|
||||
"swarmui:\n - url: https://example/x.git\n requires: docker\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
with pytest.raises(ConfigError, match="requires"):
|
||||
parse_extensions(path)
|
||||
|
||||
Reference in New Issue
Block a user