Справочник по HTTP-API LM Studio (сервер локальных моделей, обычно порт 1234). Используй когда надо программно управлять моделями на LM Studio - узнать что загружено, загрузить или выгрузить модель, задать длину контекста и параллельность, отключить размышления модели, - а также при диагностике: модель thinking/reasoning не отключается, enable_thinking игнорируется, ответ приходит пустым а весь бюджет уходит в reasoning_content, запросы падают с HTTP 500 на холодном старте или при загрузке кр...
Scanned 9/2/2026
Install to Claude Code
npx -y skills add Desko77/claude-code-skills-1c --skill lmstudio-api --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Lmstudio Api?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/desko77-lmstudio-api)More formats (shields.io, HTML) on the badges page.
---
name: lmstudio-api
description: "Справочник по HTTP-API LM Studio (сервер локальных моделей, обычно порт 1234). Используй когда надо программно управлять моделями на LM Studio - узнать что загружено, загрузить или выгрузить модель, задать длину контекста и параллельность, отключить размышления модели, - а также при диагностике: модель thinking/reasoning не отключается, enable_thinking игнорируется, ответ приходит пустым а весь бюджет уходит в reasoning_content, запросы падают с HTTP 500 на холодном старте или при загрузке крупной модели, ошибка Context size has been exceeded, Model is unloaded, эндпоинт отвечает 404 или 200 с телом Unexpected endpoint, неверно определяется длина контекста. Содержит проверенные схемы эндпоинтов v1, параметр reasoning, особенности протокола и замеры на 30-35B."
---
# LM Studio API
Проверено вживую на LM Studio 0.4.x, 2026-08-07, локальный сервер на порту 1234
(AMD Strix Halo, 128 ГБ единой памяти). Замеры в конце - на `qwen3-vl-30b-a3b-instruct`.
## Три поверхности API
| Поверхность | Путь | Назначение |
|---|---|---|
| OpenAI-совместимая | `/v1/*` | инференс: `chat/completions`; `models` отдает только КАТАЛОГ скачанных |
| Нативная v0 (устаревшая) | `/api/v0/*` | `models` с полем `state`; управления моделями нет |
| Нативная v1 | `/api/v1/*` | с LM Studio 0.4.0, рекомендуемая: инференс + управление моделями |
Anthropic-совместимые эндпоинты тоже заявлены в доках, не проверялись.
## Эндпоинты v1
```
POST /api/v1/chat (НЕ /api/v1/chat/completions)
GET /api/v1/models
POST /api/v1/models/load
POST /api/v1/models/unload
POST /api/v1/models/download
GET /api/v1/models/download/status
```
**`load`**: обязателен `model` - точный ключ модели. Опционально `context_length`,
`eval_batch_size`, `flash_attention`, `num_experts`, `offload_kv_cache_to_gpu`,
`echo_load_config`. Всегда ставить `echo_load_config: true` - ответ вернет фактически
примененный конфиг, и сразу видно, что сервер проигнорировал.
Ответ: `{"type", "instance_id", "load_time_seconds", "status": "loaded", "load_config": {...}}`.
Вызов БЛОКИРУЮЩИЙ - возвращается по факту загрузки, отдельно опрашивать готовность не нужно.
**`unload`**: обязателен `instance_id`, НЕ имя модели. Берется из `loaded_instances[].id`
ответа `GET /api/v1/models`.
**`GET /api/v1/models`** отдает `{"models":[...]}` с полями `key`, `type`, `publisher`,
`architecture`, `quantization`, `size_bytes`, `max_context_length`, `capabilities`,
`loaded_instances[]`. У инстанса - `id` и `config` с фактическими `context_length`,
`parallel`, `flash_attention`, `eval_batch_size`, `num_experts` и прочим.
Пустой `loaded_instances` = модель не загружена.
## Инференс: два эндпоинта, и они НЕ равнозначны
| | `/v1/chat/completions` (OpenAI) | `/api/v1/chat` (родной) |
|---|---|---|
| поле ввода | `messages` | `input` |
| схема | лишние ключи проглатывает | строгая, лишний ключ -> 400 `unrecognized_keys` |
| управление reasoning | НЕТ (см. ниже) | `reasoning` первым классом |
| ответ | `choices[].message.content` | `output[].content` |
| статистика | `usage` | `stats` c `reasoning_output_tokens` |
Тело родного вызова и ответ:
```json
{"model": "...", "input": "<весь промпт строкой>", "reasoning": "off"}
{"model_instance_id": "...",
"output": [{"type": "message", "content": "Париж"}],
"stats": {"input_tokens": 22, "total_output_tokens": 4, "reasoning_output_tokens": 0,
"tokens_per_second": 64.8, "time_to_first_token_seconds": 1.79}}
```
## Как ВЫКЛЮЧИТЬ размышления (thinking / reasoning)
**`chat_template_kwargs: {"enable_thinking": false}` НЕ РАБОТАЕТ.** Для GGUF линейки qwen3.x
в LM Studio этот параметр не прокидывается - зарегистрированный баг
(lmstudio-ai/lmstudio-bug-tracker issue #1990). Замер на `qwen/qwen3.6-35b-a3b`: с флагом и
без него результат идентичен - весь бюджет `max_tokens` уходит в размышления,
`finish_reason=length`, `content` пуст, ответа нет вообще.
Это опасно вдвойне: многие клиенты при пустом `content` подставляют `reasoning_content`.
Тогда в парсер уезжает текст размышлений вместо ответа, и задача со строгим форматом (JSON)
не падает с ошибкой, а тихо возвращает мусор.
**Рабочий способ - параметр `reasoning` на `/api/v1/chat`:**
```json
{"model": "qwen/qwen3.6-35b-a3b", "input": "...", "reasoning": "off"}
```
Тот же запрос через родной эндпоинт: `reasoning_output_tokens: 0`, чистый ответ в `content`,
5.8 с против полного провала на OpenAI-совместимом пути.
**Допустимые значения зависят от МОДЕЛИ.** Схема эндпоинта принимает
`off | low | medium | high | on`, но конкретная модель может поддерживать лишь часть:
`qwen3.6` отвечает 400 `Reasoning setting 'low' is not supported by model ... Supported
settings: 'off', 'on'`. Источник истины - `capabilities.reasoning.allowed_options` в
`GET /api/v1/models`. Модель без блока `reasoning` в capabilities (варианты Instruct,
например `qwen3-vl-30b-a3b-instruct`) не думает в принципе и параметра не требует.
Практический вывод: **не отбраковывать reasoning-модели** из-за "неотключаемого thinking" -
через родной эндпоинт они полностью управляемы. Отбраковка оправдана, только если клиент
намертво привязан к OpenAI-совместимому пути.
## Грабли протокола
**Неизвестный путь отдает HTTP 200, а не 404.** Тело при этом
`{"error":"Unexpected endpoint or method. (GET /path)"}`. Код ответа НЕ доказывает
существование маршрута - читать тело обязательно.
**Несоответствие метода отдает 404, а не 405.** GET по POST-эндпоинту выглядит как
"маршрута нет". Зондировать наличие маршрута GET-ом бесполезно и приводит к ложному выводу.
Правильно: POST с пустым телом - валидатор вернет 400 со схемой
(`Missing required field 'model'`), и это доказывает, что маршрут есть.
**`/api/v0/models` не отдает `loaded_context_length` у незагруженной модели** - поля просто
нет в объекте. Идиома `x.get("loaded_context_length") or x.get("max_context_length")` молча
подставит архитектурный потолок (262144 вместо реальных 32768) и обрушит расчет бюджета.
Гейтить строго по `state == "loaded"` (v0) или по непустому `loaded_instances` (v1).
**`load` может вернуть 500 на крупной модели, хотя загрузка при этом состоится.** Замер:
модель на 37 ГБ отдала HTTP 500 через 138 секунд (клиентский таймаут был 900 - обрывал не
клиент), а через интерфейс та же модель загрузилась штатно. Похоже на внутренний предел
сервера на длительность загрузки. Следствие для кода: **после неуспешного `load` не объявлять
модель недоступной сразу** - перечитать `GET /api/v1/models` и посмотреть `loaded_instances`,
загрузка могла завершиться уже после ответа с ошибкой. Иначе уйдешь в фолбэк на модели,
которая через полминуты будет готова.
**Идентификатор модели должен быть точным, включая префикс публикатора.** Вызов
`qwen3.6-35b-a3b` вместо `qwen/qwen3.6-35b-a3b` заставит LM Studio считать это другой моделью
и загрузить дубликат.
**WebSocket-namespace SDK доступны по сети**: `/llm`, `/system`, `/embedding`, `/files`,
`/repository`, `/diagnostics` - рукопожатие проходит с удаленной машины. На этом канале
работают `lms` CLI и SDK (`lmstudio-python`, `lmstudio-js`). Для управления моделями он
больше не нужен - хватает REST v1.
## Параллельность и контекст
**Unified KV cache: слоты параллелизма делят ОДНО окно контекста.** Действует
`параллельность * (промпт + max_tokens) <= context`, а НЕ `промпт + max_tokens <= context`.
Игнорирование дает HTTP 400 `Context size has been exceeded`.
Планировать бюджет только от ФАКТИЧЕСКОГО `context_length` загруженного инстанса. Считать по
паспортному `max_context_length` нельзя: разрыв бывает восьмикратным.
Серверный потолок одновременных предсказаний - поле `parallel` в конфиге инстанса, оно же
Max Concurrent Predictions в интерфейсе. **Оно ЗАДАЕТСЯ через `load`, хотя в документации
эндпоинта не указано** - проверено: `{"model": ..., "context_length": 65536, "parallel": 8}`
применяется, эхо конфига и интерфейс показывают 8. То есть менять его руками в GUI не нужно,
профиль загрузки полностью задается из кода.
Больше слотов не значит быстрее. Замер на 30B (Strix Halo): при параллельности 4 и 8
агрегированная пропускная способность одинакова (46.0 и 47.4 ток/с) - железо насыщается уже
на четырех, лишние слоты только размазывают ту же полосу. Хуже того, при отправке всех
запросов разом стена равна САМОМУ ДОЛГОМУ ответу, тогда как очередь на меньшем числе слотов
сглаживает разброс. Сравнивая режимы, нормируй на фактически сгенерированные токены: при
ненулевой температуре один и тот же вход дает разный объем вывода (наблюдалось расхождение
27% между прогонами), и сравнение по "стене" без нормировки врет.
## Холодный старт: параллельные запросы в незагруженную модель отбиваются
Замер на 30B, 4 одновременных запроса в выгруженную модель:
```
JIT (без явной загрузки) успешно 1 из 4, три отказа HTTP 500 за 0.0-0.1 с
явный load, затем те же 4 успешно 4 из 4, отказов нет
```
Первый запрос инициирует JIT-загрузку, остальные сервер отбивает, пока модель грузится.
**Тело пятисотки - generic HTML Node, без JSON и без кода ошибки:**
```html
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Error</title></head>
<body><pre>Internal Server Error</pre></body></html>
```
Отличить "модель еще грузится" от настоящего сбоя по тексту НЕВОЗМОЖНО. Единственный
надежный признак - время ответа: отказ неготовности приходит за доли секунды, тогда как
настоящий запрос к 30B идет десятки секунд. Правило: HTTP 500 быстрее секунды трактовать
как неготовность - убедиться в загрузке и повторить, в счетчик сбоев не засчитывать.
**Правильный порядок работы с моделью:**
```
1. GET /api/v1/models - есть ли в каталоге, есть ли loaded_instances
2. загружена -> взять context_length из конфига инстанса
3. не загружена -> POST /api/v1/models/load (блокирующий, ждет сам)
4. загрузка не удалась -> вот теперь модель действительно недоступна
5. бюджет считать от фактического context_length
6. первый запрос отправить в одиночку, потом выходить на параллельность
```
## Жизненный цикл модели: грузить один раз на всю работу
Загрузка стоит десятки секунд, выгрузка - две. Любая перезагрузка между стадиями или между
файлами серии - чистая потеря времени.
```
Начало работы (всей серии, не одного файла):
модель уже загружена -> ИСПОЛЬЗОВАТЬ КАК ЕСТЬ, бюджет считать от ЕЕ контекста
не загружена -> load один раз, запомнить instance_id как свой
Во время обработки:
не выгружать и не перезагружать ничего - ни между стадиями, ни между файлами
Конец:
РАБОЧУЮ модель конвейера ОСТАВИТЬ загруженной, TTL уберет сам
РАЗОВУЮ модель из бенчмарка ВЫГРУЗИТЬ СРАЗУ - см. ниже
чужие инстансы не трогать никогда
```
**Исключение из "оставить загруженными": модель, поднятая под разовый замер.** Правило держать
инстанс теплым написано для рабочих моделей конвейера, где перезагрузка дорога и повторится.
К модели, которую подняли один раз ради сравнения и отвергли, оно не относится: она занимает
память до истечения TTL и деградирует всех соседей.
Цена ошибки измерена 07.08.2026 на общем боксе. `qwen3.6-35b-a3b` осталась после ночного теста
в Q8_0 с `context_length 262144` и `parallel 4` - веса 35 ГБ плюс KV-кэш под четверть миллиона
токенов на четыре слота. Соседняя gemma, обслуживающая интерактивный голосовой ввод, отвечала
**34.5 с на 8 токенов**; после выгрузки лишней модели - 12.3 с на холодном KV и **0.2 с на
теплом**. Деградация в сотню раз держалась часами и выглядела как "сервер тормозит", а не как
чей-то забытый инстанс.
Отсюда два следствия. Первое: закончил замер - выгрузи свой инстанс тем же ходом, не откладывая
на TTL. Второе: **грузя модель под замер, задавать `context_length` явно**, по реальной нужде
замера. Дефолт берет паспортный максимум (у MoE это сотни тысяч токенов), и KV-кэш съедает
больше, чем сами веса.
**Подстраивать себя под модель, а не модель под себя.** Если модель уже загружена с "неудобным"
контекстом - считать параллельность от него, а не перезагружать ради круглого числа. Сохраненный
конфиг мог быть выставлен осознанно (наблюдалось `context_length: 50176` у 32B - число не круглое
и явно не дефолтное), и модель может обслуживать чужую задачу. Перезагрузка оправдана, только если
существующего окна не хватает даже на ОДИН запрос, и делать ее молча нельзя.
Несколько крупных моделей спокойно живут рядом при достаточной памяти: 30B + 26B + 32B заняли
около 65 ГБ из 128 и работали одновременно. Значит "свопов" между стадиями конвейера может не
быть вовсе - проверять со-резидентность до того, как городить оптимизацию порядка стадий.
## Правило на общем сервере
Сервер моделей может обслуживать не только текущую задачу (типовой случай - параллельно
работающий голосовой ввод на своей модели).
**Выгружать разрешено только те инстансы, которые загрузил ты сам в этом прогоне.**
Все, что застали загруженным, не трогать. Перед `unload` сверять `instance_id` со своим
списком, а не искать модель по имени.
Авто-вытеснение LM Studio этим правилом не управляется: загрузка своей модели в принципе
может выбить чужую без явного `unload`. На боксе с большой памятью почти не грозит - на
128 ГБ 30B и 26B держались одновременно, вытеснения не было. На тесной машине проверять
состояние чужих моделей ПОСЛЕ своей загрузки и сообщать пользователю, если что-то выгрузилось.
## Замеры (qwen3-vl-30b-a3b-instruct, Q6_K, Strix Halo 128 ГБ)
```
загрузка 30B (ctx 32768) 29-39 с выгрузка 1.8-3.3 с
загрузка 32B (ctx 50176) 16-19 с
контекст при загрузке 32768 (паспортный max 262144)
```
Зрение по кадрам-скриншотам, `prompt_tokens` ПОСТОЯНЕН для всех кадров одного видео (зависит
от разрешения, не от содержимого - наблюдалось 1196 и на разреженном, и на плотном экране):
```
редкий кадр (список участников) completion 63-216, латентность 19-27 с
плотный кадр 1С completion 560-4904, латентность 45-144 с
4 параллельно, редкие кадры 6 с на кадр амортизированно
4 параллельно, плотные кадры 25.5 с на кадр, ~46 ток/с агрегированно
```
Текстовая задача (маппинг спикеров, вход ~7200 токенов, выход ~80):
```
qwen2.5-32b-instruct (плотная) 78.5 с
qwen3-vl-30b-a3b-instruct (MoE) 21.8 с
qwen/qwen3.6-35b-a3b, reasoning=off 5.8 с
qwen/qwen3.6-35b-a3b через OpenAI-путь провал: весь бюджет в reasoning, ответа нет
```
Со-резидентность: три модели одновременно (30B ctx 32768 + 35B ctx 262144 + 26B ctx 32000),
суммарно порядка 80 ГБ на 128 ГБ единой памяти - работают без вытеснения. Footprint сильно
зависит от ВЫБРАННОГО контекста, а не только от веса: 35B весом 35.2 ГБ при окне 262144
занимает 37.8 ГБ. Грузя модель сам, задавай окно под задачу, а не паспортный максимум.
Выводы для планирования. Латентность одного запроса и пропускная способность расходятся
вчетверо - оценивать стоимость стадии по латентности значит завысить в разы. И MoE-модели
на текстовых задачах дают кратный выигрыш над плотными при сопоставимом размере.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!