Berth — брокер аренды видеопамяти для локальных ИИ-моделей
📂 Исходный код на GitHubБрокер аренды видеопамяти для NVIDIA: локальные ИИ-движки запускаются через berth run и получают карту по очереди с приоритетами, играм и creative-приложениям соответствуют отдельные правила, есть шлюз с OpenAI-совместимым API, веб-панель harbor и сборка моделей с Hugging Face и CivitAI. Написано на Go 1.27, лицензия Apache-2.0.
Berth — брокер аренды видеопамяти для локальных ИИ-моделей
На одной видеокарте NVIDIA часто хотят работать сразу несколько программ: чат-движок, генератор картинок, синтез речи, иногда игра. Но они не умеют делиться памятью. Кто загрузился первым, тот занял всю VRAM, а остальные падают с ошибкой «out of memory».
Проект Berth ставит между движками и картой посредника. Он выдаёт видеопамять по очереди и ждёт, пока прошлый владелец её освободит. Это один бинарник на Go 1.27: без облачного аккаунта, без базы данных, без менеджера пакетов. Лицензия Apache-2.0, статус — alpha (v0.2).
Как проходит передача карты
Каждый движок спрашивает у брокера разрешения. Это делает команда berth run, шлюз, API ComfyUI или запрос на игровую сессию. Дальше всё идёт по четырём шагам:
- Запрос встаёт в очередь по приоритету:
hold,foreground,interactive,batch. - Текущий владелец заканчивает свой ответ или задание, после чего Berth его выгружает.
- Драйвер NVIDIA показывает, что память действительно освободилась. Просто завершившийся процесс может ещё какое-то время держать VRAM, поэтому Berth ждёт подтверждения от драйвера, а не просто выхода процесса.
- Аренда выдаётся следующему движку, и запрос, который ждал, начинает отдавать результат.
Выгрузка тоже идёт в три захода: сначала штатная команда движка (у llama-swap — это его собственный unload-URL), затем SIGTERM всей группе процессов, и только потом SIGKILL. Между шагами есть пауза, чтобы процесс успел завершиться сам.
Если процесс занимает видеопамять, но аренды не имеет, Berth показывает его отдельно: PID, имя и объём памяти. Убивать такой процесс он не станет.
Приоритеты и отказы
Запрос равного или более высокого приоритета просит текущего владельца освободить карту. Запрос меньшего приоритета ждёт и получает отказ с указанием, кто именно держит карту (код выхода 75). Падения с out of memory при этом не происходит.
Отдельно решается проблема «пинг-понга». Если два клиента одновременно просят разные движки, карта начинает прыгать туда-сюда, и оба запроса падают. Berth считает это «жонглированием» и запрещает его: рейтинг движков решает, кто победит, а проигравший просто ждёт. Рейтинг задаётся в конфиге:
[priority]
order = ["llama-swap", "strata", "comfyui"]
juggle_window = "15s"
Если порядок не задан, первым идёт тот движок, который провёл на карте больше всего часов за последнюю неделю.
ComfyUI передаётся на границе задания: Berth дожидается, пока очередь картинок опустеет, и только тогда освобождает карту. Работающий процесс он не обрывает.
Если демон перезапускается или обновляется, запущенные движки остаются живы и снова привязываются к брокеру по PID и времени старта.
Какие движки брокер находит сам
Berth опрашивает стандартные порты и читает состояние движков:
| Движок | Порт по умолчанию | Что читает |
|---|---|---|
llama.cpp (llama-server) |
8080 | токены, скорость, очередь, окно контекста, слоты KV-кэша |
| llama-swap | 8080 | запущенные модели, журнал запросов, окно контекста по моделям |
| Ollama | 11434 | загруженные модели и их видеопамять |
| vLLM | 8000 | модели, окно контекста, очередь из /metrics |
| SGLang | 30000 | модели, окно контекста, очередь |
| ComfyUI | 8188 | выполняющиеся и отложенные задания |
| Kokoro-FastAPI | 8880 | состояние, модели, голоса |
| LM Studio | 1234 | загруженные модели, длина контекста |
| KoboldCpp | 5001 | модель, длина контекста |
| TabbyAPI | 5000 | модели, длина контекста |
| LocalAI | 8080 | модели, токены |
Любой OpenAI-совместимый сервер тоже подходит. Свой сервер можно описать вручную в конфиге — с любым портом и любым адресом. Если движку нужен ключ, в конфиге указывается имя переменной окружения, а сам ключ туда не попадает.
Игры и тяжёлые приложения
Berth умеет отдавать карту игре. Пока вы в Steam, Lutris или gamescope на Linux (или в игре из Epic, GOG, EA, Ubisoft, Xbox, Battle.net на Windows), ИИ-движки выгружаются, и карта ждёт вас. На 15 секунд дольше, чтобы лаунчер не начал «пинг-понг» с игрой.
Приложения для работы с видео и графикой обрабатываются мягче. Blender, DaVinci Resolve, OBS, Kdenlive, Premiere и After Effects не выгружаются. Berth просто считает, сколько видеопамяти они держат, и зачитывает это в общий счёт.
Свои правила описываются в berth.toml:
[[claim]]
name = "training"
cmdline = 'train\.py'
action = "hold"
linger = "30s"
Команда berth hold освобождает карту под ваш звонок или игру, которую вы запускаете сами. Ctrl-C возвращает карту. А berth run --pinned даёт приложению аренду, которую нельзя прервать, — она живёт до выхода процесса.
Один порт вместо десяти
Команда berth gateway поднимает один OpenAI-совместимый порт. Вы называете модель в запросе, и Berth сам запускает нужный движок, выгружает того, кто сейчас держит карту, ждёт загрузки и отдаёт вам поток ответа. Схема простая:
{ "baseUrl": "http://127.0.0.1:9799/v1", "apiKey": "!berth key" }
Шлюз умеет и картинки: запрос в ComfyUI ставится в ту же очередь и ждёт ту же аренду. Есть «пробуждающие» страницы: закладка на веб-интерфейс остановленного движка показывает, кто держит карту, и кнопку Start. Сама страница карту не занимает — кнопка запрашивает аренду.
На всех маршрутах требуется bearer-ключ, слушает шлюз только локальный интерфейс, а запросы с браузерным Origin отклоняются: с сайта нельзя запустить движок на вашей машине.
Замеры на опорной RTX 4090 при переключении между NInfer через llama-swap и Strata с контекстом 262K:
| Переключение | Освобождение карты | Ответ после |
|---|---|---|
| Strata на пустой карте | — | 106 с |
| Strata → NInfer | Strata освободил за 2 с | 41 с |
| NInfer → Strata | NInfer освободил за 0,6 с | 110 с |
Ни в один момент два движка не держали карту одновременно.
Модели
berth pull hf:org/repo:Q4_K_M скачивает модель с Hugging Face, а berth pull civitai:VERSION_ID — с CivitAI. Каждый файл сначала качается во временное имя и переезжает на место только после того, как размер и sha256 совпали с хешем, который опубликовал сайт. При несовпадении файл удаляется и команда падает. Прерванную загрузку можно продолжить повторным запуском команды.
Перед скачиванием Berth проверяет свободное место в каталоге и отказывается работать, если после загрузки останется меньше доли, заданной в конфиге (по умолчанию — десятая часть). Каталоги моделей делятся на полки: чат, картинки, звук. Команда berth models читает заголовки GGUF-файлов и показывает, что именно лежит на диске, с квантизацией, размером, числом параметров, окном контекста и датой последнего использования. berth storage показывает свободное место по каждому каталогу, berth verify перепроверяет хеши офлайн, а berth models export ollama превращает уже скачанный GGUF в модель для Ollama.
Наблюдение
berth harbor открывает веб-панель на 127.0.0.1:9798. В ней видна доска аренд, счётчик предотвращённых столкновений, график видеопамяти по владельцам, лента событий и таблица движков. Панель работает только на чтение и по умолчанию доступна лишь с самой машины.
Телеметрия встроенная: память, загрузка, температура, питание и частоты по каждой карте, токены в секунду, очередь, задержки в проценталях, загрузка KV-кэша, счётчик токенов и таблица лидеров по моделям. История за 30 дней лежит на диске. Есть пороговая система оповещений, /metrics для Prometheus и экспорт в CSV или JSON. Сам демон ест около 0,2% ядра и 21 МБ памяти, панель — около 0,4% ядра и 23 МБ.
Весь интерфейс командной строки устроен одинаково: --format json|yaml, результат в stdout, диагностика в stderr, коды выхода 0, 1 и 2. Команда berth skill install ставит агентам инструкцию просить карту у Berth, а не убивать процессы.
Установка
Готовый бинарник или сборка из исходников:
go install github.com/T-Crypt/berth/cmd/berth@latest
mkdir -p ~/.config/systemd/user
cp contrib/berthd.service contrib/berth-harbor.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now berthd berth-harbor
Дальше нужно обернуть команды запуска движков:
berth run --as <имя> --priority <batch|interactive|foreground> -- <команда движка>
Готовые рецепты для llama-swap, llama.cpp, Ollama, ComfyUI, vLLM, SGLang и своих серверов лежат в contrib/INTEGRATION.md. Есть и режим установки через агента: можно скормить коду агенту текст из README, и он сам всё поставит, показав список изменений и способ их отменить.
Ограничения
Поддерживается Linux с картой NVIDIA. Windows 10 и 11 работают в экспериментальном режиме: аренда, определение процессов и игровые правила есть, но драйвер под WDDM не показывает память по процессам, поэтому там нужны аренды на всю карту. Карты AMD, Intel и Apple пока не поддерживаются, а на несколько GPU пока работает один демон на одну карту.
Чего Berth не делает
Он не убивает процессы, которые не запускал. Он не выдаёт карту только потому, что процесс завершился, — драйвер должен подтвердить освобождение памяти. Он не считает память занятой, если она лишь предположительно свободна. И он не требует облачного аккаунта и не отправляет телеметрию наружу.
Лицензия — Apache-2.0. Текст и условия лежат в LICENSE, сторонние компоненты — в THIRD_PARTY_NOTICES.
Источник: https://github.com/T-Crypt/berth