# Ответы и потоковая передача

> Основной диалоговый интерфейс LMSP с запросами и элементами вывода в стиле Responses, синхронной и потоковой генерацией, типами событий SSE, идентификаторами событий, переподключением и вызовами инструментов.

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

## 6.1 Запрос {#request}

```http
POST /lmsp/v1/responses
Authorization: Bearer <token>
Content-Type: application/json
Idempotency-Key: 01J8M5T7Z2K4N6Q8S0V3X5B9DG
LMSP-Version: 1.0
```

```json
{
  "model": "qwen3:14b-q4_k_m",
  "input": [
    {
      "type": "message",
      "role": "user",
      "content": [
        {
          "type": "input_text",
          "text": "Describe a local model service in one sentence."
        }
      ]
    }
  ],
  "stream": false,
  "max_output_tokens": 512,
  "temperature": 0.7,
  "response_format": {
    "type": "text"
  },
  "store": true
}
```

Основной протокол использует структуру `input` и `output` в стиле Responses, а не простой список строк сообщений, чтобы одна форма запроса могла нести:

- многоходовые сообщения;
- текст, изображения, аудио и файлы;
- вызовы инструментов и их результаты;
- структурированный вывод;
- сводки рассуждений;
- мультимодальный вывод.

LMSP 1.0 определяет только текстовое содержимое. Типы содержимого для изображений, аудио и файлов зарезервированы для будущих версий.

| Поле | Обязательное | Значение |
| --- | --- | --- |
| `model` | Да | Идентификатор используемой модели. |
| `input` | Да | Массив элементов ввода. Элемент `message` имеет `role` (`system`, `user` или `assistant`) и массив частей `content`, например `input_text`. Использование инструментов добавляет элементы `function_call` и `function_call_output` ([раздел 6.5](https://madeforagent.com/ru/lmsp/1.0/responses.md#tools)). |
| `stream` | Нет | `true`, чтобы получать события, отправляемые сервером. По умолчанию `false`. |
| `max_output_tokens` | Нет | Верхняя граница числа генерируемых токенов. |
| `temperature` | Нет | Температура сэмплирования. |
| `response_format` | Нет | `{"type": "text"}` (по умолчанию) или `{"type": "json_schema", "json_schema": {"name": "...", "schema": {...}}}` для структурированного вывода. Требует возможности `structured_output`. |
| `tools`, `tool_choice` | Нет | Функции-инструменты, которые может вызывать модель ([раздел 6.5](https://madeforagent.com/ru/lmsp/1.0/responses.md#tools)). Требует возможности `tools`. |
| `store` | Нет | Просит сервис сохранить ответ, чтобы его можно было получить позже и использовать как `previous_response_id`. По умолчанию `false`. Игнорируется сервисами без возможности `conversations`. |
| `previous_response_id` | Нет | Продолжает с сохранённого ответа. Требует возможности `conversations`. |
| `extensions` | Нет | Частные расширения ([раздел 9.1](https://madeforagent.com/ru/lmsp/1.0/common-rules.md#versioning)). |

Запрос, которому нужна возможность, отсутствующая у модели, отклоняется с `400` и кодом `unsupported_capability`. Запрос, который не помещается в контекстное окно, отклоняется с `400` и кодом `context_length_exceeded`.

Сервис, умеющий хранить диалоги, **МОЖЕТ** также поддерживать `previous_response_id`. В противном случае каждый запрос **ДОЛЖЕН** нести весь контекст. Сервис **НЕ ДОЛЖЕН** полагаться на то, что состояние диалога хранится в HTTP-соединении.

## 6.2 Синхронный ответ {#sync}

Если `stream` равен `false`, сервис возвращает `200 OK` и объект ответа после завершения генерации.

```json
{
  "id": "resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF",
  "object": "response",
  "created_at": 1794200000,
  "status": "completed",
  "model": "qwen3:14b-q4_k_m",
  "output": [
    {
      "id": "msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE",
      "type": "message",
      "role": "assistant",
      "content": [
        {
          "type": "output_text",
          "text": "A local model service is a resident interface layer that discovers, manages and calls models on the user's own device."
        }
      ]
    }
  ],
  "usage": {
    "input_tokens": 28,
    "output_tokens": 31,
    "total_tokens": 59
  },
  "error": null,
  "incomplete_details": null
}
```

| Поле | Значение |
| --- | --- |
| `id` | Идентификатор ответа, например `resp_...`. |
| `object` | Всегда `"response"`. |
| `created_at` | Время создания в секундах с начала эпохи Unix. |
| `status` | `in_progress`, `completed`, `incomplete`, `failed` или `cancelled`. Синхронный ответ возвращается в конечном статусе. |
| `model` | Идентификатор модели, создавшей ответ. |
| `output` | Массив элементов вывода. |
| `usage` | Количество токенов: `input_tokens`, `output_tokens` и `total_tokens`. |
| `error` | `null` или объект проблемы ([раздел 9.3](https://madeforagent.com/ru/lmsp/1.0/common-rules.md#errors)), когда `status` равен `failed`. |
| `incomplete_details` | `null` или объект с полем `reason`, например `max_output_tokens`, когда `status` равен `incomplete`. |

Типы элементов вывода:

| Тип элемента | Структура |
| --- | --- |
| `message` | `id`, `role` (`assistant`) и `content` — массив частей `output_text`, у каждой из которых есть `text`. |
| `function_call` | `id`, `call_id`, `name` и `arguments` — строка в формате JSON. |
| `reasoning` | `id` и `summary` — массив частей `summary_text`, у каждой из которых есть `text`. |

Если генерация не удалась, синхронный запрос получает статус ошибки и документ проблемы ([раздел 9.3](https://madeforagent.com/ru/lmsp/1.0/common-rules.md#errors)), а не объект ответа. Статус `failed` встречается в потоках, задачах и сохранённых ответах.

`GET /lmsp/v1/responses/{response_id}` возвращает ответ, сохранённый с `store: true`. Он необязателен и требует возможности `conversations`. Неизвестный идентификатор даёт `404` с кодом `response_not_found`.

## 6.3 Потоковая передача {#streaming}

Чтобы получить поток, задайте `stream` в запросе:

```json
{
  "stream": true
}
```

Сервис отвечает `200 OK` с `Content-Type: text/event-stream` и отправляет семантические события. У каждого события есть `id`, имя `event` и JSON-объект `data`:

```text
id: 1
event: response.created
data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","status":"in_progress"}

id: 2
event: response.output_item.added
data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","output_index":0,"item":{"id":"msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE","type":"message","role":"assistant","content":[]}}

id: 3
event: response.output_text.delta
data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","item_id":"msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE","delta":"A local model "}

id: 4
event: response.output_text.delta
data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","item_id":"msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE","delta":"service is a resident interface layer."}

id: 5
event: response.output_item.done
data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","output_index":0,"item":{"id":"msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE","type":"message","role":"assistant","content":[{"type":"output_text","text":"A local model service is a resident interface layer."}]}}

id: 6
event: response.completed
data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","status":"completed","usage":{"input_tokens":28,"output_tokens":12,"total_tokens":40}}
```

Определены как минимум следующие события:

| Событие | Значение | Поля данных |
| --- | --- | --- |
| `response.created` | Ответ создан. | `response_id`, `status` (`in_progress`) |
| `response.output_item.added` | Начался новый элемент вывода. | `response_id`, `output_index`, `item` |
| `response.output_text.delta` | К элементу-сообщению добавлен текст. | `response_id`, `item_id`, `delta` |
| `response.output_item.done` | Элемент вывода завершён. | `response_id`, `output_index`, `item` |
| `response.completed` | Ответ завершён. Последнее событие. | `response_id`, `status`, `usage` |
| `response.incomplete` | Генерация остановлена досрочно, например по лимиту токенов. Последнее событие. | `response_id`, `status`, `incomplete_details`, `usage` |
| `response.failed` | Генерация завершилась ошибкой. Последнее событие. | `response_id`, `status`, `error` |
| `response.cancelled` | Ответ отменён. Последнее событие. | `response_id`, `status` |
| `error` | Проблема, не связанная с одним ответом, после которой поток продолжаться не может. | `error` — объект проблемы |

Сервис закрывает поток после последнего события или события `error`.

Сервисы **МОГУТ** отправлять дополнительные события, например для аргументов вызовов инструментов, изображений, аудио и дельт структурированного JSON. Клиенты **ДОЛЖНЫ** игнорировать неизвестные им типы событий.

Если клиент закрывает соединение потокового запроса, сервису **СЛЕДУЕТ** прекратить генерацию.

## 6.4 Идентификаторы событий и переподключение {#reconnection}

Идентификатор `id` события **ДОЛЖЕН** быть целым порядковым номером, строго возрастающим в пределах ответа и начинающимся с 1.

После обрыва соединения клиент возобновляет получение, отправив последний полученный идентификатор. Он делает это через заголовок:

```http
Last-Event-ID: 2
```

или через параметр запроса:

```http
GET /lmsp/v1/tasks/{task_id}/events?after=2
```

Оба варианта направляются на эндпоинт событий задачи ([раздел 7.4](https://madeforagent.com/ru/lmsp/1.0/tasks.md#events)). Сервис повторно отправляет только те события, чей порядковый номер больше 2. Если указаны оба, приоритет у `after`.

Если запрошенные события старше срока хранения, сервис отвечает `410 Gone` с ошибкой `event_history_expired`. Тогда клиент получает итоговый результат запросом `GET /lmsp/v1/tasks/{task_id}`.

Ответ, переданный потоком напрямую из `POST /lmsp/v1/responses`, принадлежит этому HTTP-соединению и возобновлён быть не может. Клиенту, которому нужен возобновляемый вывод, **СЛЕДУЕТ** создать [задачу](https://madeforagent.com/ru/lmsp/1.0/tasks.md) и следить за её событиями.

## 6.5 Вызовы инструментов {#tools}

LMSP 1.0 определяет функции-инструменты, которые выполняет клиент. Сам сервис инструменты не запускает. Область доступа `tools:execute` зарезервирована для расширений, которые это делают ([раздел 10.3](https://madeforagent.com/ru/lmsp/1.0/security.md#scopes)).

Сначала клиент перечисляет инструменты. У каждой записи есть `type` со значением `function`, `name`, `description` и `parameters` — объект JSON Schema. `tool_choice` принимает значения `auto` (по умолчанию), `none`, `required` или `{"type": "function", "name": "get_weather"}`.

```json
{
  "model": "qwen3:14b-q4_k_m",
  "input": [
    {
      "type": "message",
      "role": "user",
      "content": [
        {
          "type": "input_text",
          "text": "What is the weather in Hangzhou?"
        }
      ]
    }
  ],
  "tools": [
    {
      "type": "function",
      "name": "get_weather",
      "description": "Get the current weather for a city.",
      "parameters": {
        "type": "object",
        "properties": {
          "city": { "type": "string" }
        },
        "required": ["city"]
      }
    }
  ],
  "tool_choice": "auto"
}
```

Когда модель решает вызвать инструмент, ответ содержит элемент `function_call`. Его `arguments` — строка в формате JSON. В потоке элемент приходит целиком в `response.output_item.done`.

```json
{
  "id": "resp_01J8M7B3N6Q9T2V5X8Z1C4D7FH",
  "object": "response",
  "created_at": 1794200050,
  "status": "completed",
  "model": "qwen3:14b-q4_k_m",
  "output": [
    {
      "id": "fc_01J8M7B3N8R1S4W7Y0A3E6G9JK",
      "type": "function_call",
      "call_id": "call_01J8M7B3N9T2V5X8Z1C4D7F0HM",
      "name": "get_weather",
      "arguments": "{\"city\":\"Hangzhou\"}"
    }
  ],
  "usage": {
    "input_tokens": 74,
    "output_tokens": 18,
    "total_tokens": 92
  },
  "error": null,
  "incomplete_details": null
}
```

Клиент запускает инструмент и отправляет новый запрос с полным контекстом: прежние элементы ввода, элемент `function_call` в том виде, в каком он был получен, и элемент `function_call_output` с тем же `call_id`. Его `output` — строка, обычно в формате JSON.

```json
{
  "type": "function_call_output",
  "call_id": "call_01J8M7B3N9T2V5X8Z1C4D7F0HM",
  "output": "{\"temperature_c\":17,\"condition\":\"light rain\"}"
}
```
