# Общие правила

> Правила, общие для всех эндпоинтов LMSP, включая заголовки версий и расширения, ключи идемпотентности, формат ошибок problem+json с кодами ошибок и проверку работоспособности.

Source: https://madeforagent.com/ru/lmsp/1.0/common-rules/
Language: ru
Specification: LMSP 1.0 (draft)
Updated: 2026-11-08

## 9.1 Версии и расширения {#versioning}

Каждый запрос LMSP **МОЖЕТ** нести эти заголовки:

```http
LMSP-Version: 1.0
LMSP-Client: example-client/1.2.0
```

`LMSP-Version` — версия протокола, на которую рассчитан клиент. `LMSP-Client` обозначает клиентскую программу для диагностики в виде `name/version`. Сервис **НЕ ДОЛЖЕН** менять поведение протокола в зависимости от этого заголовка.

Правила:

- `v1` в пути обозначает основную версию.
- Запрос, в котором указана неизвестная основная версия, получает ответ `426 Upgrade Required` с кодом ошибки `unsupported_protocol_version`.
- Новые необязательные поля **НЕ ДОЛЖНЫ** нарушать совместимость 1.x.
- Сервисы и клиенты **ДОЛЖНЫ** игнорировать неизвестные им поля.
- Частные расширения помещаются в объект `extensions` и именуются в обратной доменной записи:

```json
{
  "extensions": {
    "com.example.cache": {
      "enabled": true
    }
  }
}
```

## 9.2 Идемпотентность {#idempotency}

Запросы, которые что-то создают, например создание ответа, создание задачи или загрузка модели, принимают ключ идемпотентности:

```http
Idempotency-Key: <client-generated-unique-value>
```

Сервис сохраняет ключ вместе с дайджестом тела запроса и результатом:

- Тот же ключ с тем же телом возвращает исходный результат.
- Тот же ключ с другим телом получает ответ `409 Conflict` с кодом `idempotency_conflict`.

Ключ действует в пределах клиента и эндпоинта. Ключ **СЛЕДУЕТ** делать уникальным значением длиной не более 255 символов, например ULID или UUID. Сервису **СЛЕДУЕТ** хранить ключи не менее 24 часов.

Сервис **МОЖЕТ** игнорировать ключ в потоковых запросах. Клиент, которому нужны безопасные повторы, использует [задачу](https://madeforagent.com/ru/lmsp/1.0/tasks.md).

## 9.3 Ошибки {#errors}

Каждая ошибка интерфейса LMSP использует `application/problem+json` ([RFC 9457](https://www.rfc-editor.org/rfc/rfc9457)):

```json
{
  "type": "urn:lmsp:error:model-not-found",
  "title": "Model not found",
  "status": 404,
  "code": "model_not_found",
  "detail": "No local model with id 'qwen3:14b-q4_k_m'",
  "request_id": "req_01J8M9S6Y4W8C3P5R1V7Z2A0DH",
  "retryable": false
}
```

| Поле | Обязательное | Значение |
| --- | --- | --- |
| `type` | Да | URI, обозначающий вид ошибки. LMSP использует `urn:lmsp:error:`, за которым следует код с дефисами вместо подчёркиваний. |
| `title` | Да | Краткое описание, понятное человеку. |
| `status` | Да | Код состояния HTTP. |
| `code` | Да | Машиночитаемый код ошибки из списка ниже. |
| `detail` | Нет | Понятное человеку пояснение к данному случаю. Сервис **НЕ ДОЛЖЕН** помещать в него секреты, промпты или сгенерированное содержимое. |
| `request_id` | Нет | Идентификатор запроса для сопоставления с журналами сервиса. |
| `retryable` | Да | Может ли повторный запрос позже завершиться успехом. |

Типичные коды ошибок:

| Код | HTTP-статус | Можно повторить | Значение |
| --- | --- | --- | --- |
| `invalid_request` | 400 | Нет | Запрос искажён или содержит недопустимые параметры. |
| `unauthorized` | 401 | Нет | Токен отсутствует, недействителен, просрочен или отозван, либо неверен код сопряжения. |
| `forbidden` | 403 | Нет | У токена нет нужной области доступа, либо не пройдена проверка `Origin` или `Host`. |
| `model_not_found` | 404 | Нет | Ни у одной локальной модели нет такого идентификатора. |
| `model_not_ready` | 409 | Да | Модель не загружена и не загружается по требованию. |
| `model_load_failed` | 500 | Да | Не удалось загрузить модель. |
| `context_length_exceeded` | 400 | Нет | Ввод не помещается в контекстное окно модели. |
| `unsupported_capability` | 400 или 501 | Нет | Модель или сервис не поддерживают то, что нужно запросу. Нереализованный необязательный эндпоинт отвечает 501. |
| `unsupported_protocol_version` | 426 | Нет | Сервис не поддерживает запрошенную основную версию. |
| `response_not_found` | 404 | Нет | Ни у одного сохранённого ответа нет такого идентификатора. |
| `task_not_found` | 404 | Нет | Ни у одной задачи нет такого идентификатора. |
| `task_not_cancelable` | 409 | Нет | Задача уже завершилась успешно или с ошибкой. |
| `idempotency_conflict` | 409 | Нет | Ключ идемпотентности уже использован с другим телом запроса. |
| `event_history_expired` | 410 | Нет | Запрошенные события старше срока хранения. |
| `rate_limited` | 429 | Да | Слишком много запросов. |
| `resource_exhausted` | 503 | Да | На устройстве закончилась память или другой ресурс. |
| `backend_unavailable` | 503 | Да | Среда выполнения модели недоступна. |
| `server_restarted` | 503 | Да | Сервис перезапустился, пока задача была не завершена. |
| `internal_error` | 500 | По-разному | Неожиданный сбой внутри сервиса. |

Поле `retryable` является определяющим. В столбце выше показано обычное значение. Сервис **МОЖЕТ** определять дополнительные коды. Клиент **ДОЛЖЕН** обрабатывать неизвестный ему код по его HTTP-статусу и полю `retryable`.

## 9.4 Проверка работоспособности {#health}

```http
GET /lmsp/v1/health
```

```json
{
  "status": "ready",
  "service_id": "svc_01J8M4Y8K6M5E6K6Z7Z0Y8W3M2",
  "uptime_seconds": 86420,
  "loaded_models": 1,
  "active_tasks": 2,
  "storage": "ok"
}
```

| Поле | Значение |
| --- | --- |
| `status` | `starting`, `ready`, `degraded` или `stopping`. |
| `service_id` | Идентификатор сервиса из описания сервиса. |
| `uptime_seconds` | Секунды с момента запуска сервиса. |
| `loaded_models` | Число моделей в состоянии `ready`. |
| `active_tasks` | Число задач в состоянии `queued`, `loading`, `running` или `cancelling`. |
| `storage` | `ok` или `error`. |

Статус HTTP — `200 OK`, когда `status` равен `ready` или `degraded`, и `503 Service Unavailable` в остальных случаях.

Эндпоинт проверки возвращает только рабочее состояние. Он **НЕ ДОЛЖЕН** возвращать пути к моделям, промпты, токены доступа и другие чувствительные сведения.
