# Responses and streaming

> The LMSP core conversation interface, with Responses-style requests and output items, synchronous and streaming generation, server-sent event types, event ids, reconnection and tool calls.

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

## 6.1 Request {#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
}
```

The core protocol uses a Responses-style `input` and `output` structure instead of a plain list of message strings, so that a single request shape can carry:

- multi-turn messages;
- text, images, audio and files;
- tool calls and tool results;
- structured output;
- reasoning summaries;
- multimodal output.

LMSP 1.0 defines text content only. Image, audio and file content types are reserved for later versions.

| Field | Required | Meaning |
| --- | --- | --- |
| `model` | Yes | Id of the model to use. |
| `input` | Yes | Array of input items. A `message` item has a `role` (`system`, `user` or `assistant`) and a `content` array of parts, such as `input_text`. Tool use adds `function_call` and `function_call_output` items ([section 6.5](https://madeforagent.com/lmsp/1.0/responses.md#tools)). |
| `stream` | No | `true` to receive server-sent events. Defaults to `false`. |
| `max_output_tokens` | No | Upper bound on the number of generated tokens. |
| `temperature` | No | Sampling temperature. |
| `response_format` | No | `{"type": "text"}` (the default), or `{"type": "json_schema", "json_schema": {"name": "...", "schema": {...}}}` for structured output. Needs the `structured_output` capability. |
| `tools`, `tool_choice` | No | Function tools the model may call ([section 6.5](https://madeforagent.com/lmsp/1.0/responses.md#tools)). Needs the `tools` capability. |
| `store` | No | Asks the service to keep the response, so it can be retrieved later and used as `previous_response_id`. Defaults to `false`. Ignored by services without the `conversations` capability. |
| `previous_response_id` | No | Continues from a stored response. Needs the `conversations` capability. |
| `extensions` | No | Private extensions ([section 9.1](https://madeforagent.com/lmsp/1.0/common-rules.md#versioning)). |

A request that needs a capability the model does not have is rejected with `400` and `unsupported_capability`. A request that does not fit in the context window is rejected with `400` and `context_length_exceeded`.

A service that can store conversations **MAY** also support `previous_response_id`. Otherwise every request **MUST** carry the full context. A service **MUST NOT** rely on the HTTP connection to hold conversation state.

## 6.2 Synchronous response {#sync}

When `stream` is `false`, the service returns `200 OK` with a response object after generation has finished.

```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
}
```

| Field | Meaning |
| --- | --- |
| `id` | Response identifier, such as `resp_...`. |
| `object` | Always `"response"`. |
| `created_at` | Creation time, in seconds since the Unix epoch. |
| `status` | `in_progress`, `completed`, `incomplete`, `failed` or `cancelled`. A synchronous response is returned in a final status. |
| `model` | Id of the model that produced the response. |
| `output` | Array of output items. |
| `usage` | Token counts: `input_tokens`, `output_tokens` and `total_tokens`. |
| `error` | `null`, or a problem object ([section 9.3](https://madeforagent.com/lmsp/1.0/common-rules.md#errors)) when `status` is `failed`. |
| `incomplete_details` | `null`, or an object with a `reason` such as `max_output_tokens` when `status` is `incomplete`. |

Output item types:

| Item type | Shape |
| --- | --- |
| `message` | `id`, `role` (`assistant`) and `content`, an array of `output_text` parts that each have a `text`. |
| `function_call` | `id`, `call_id`, `name` and `arguments`, a JSON-encoded string. |
| `reasoning` | `id` and `summary`, an array of `summary_text` parts that each have a `text`. |

If generation fails, a synchronous request is answered with an error status and a problem document ([section 9.3](https://madeforagent.com/lmsp/1.0/common-rules.md#errors)), not with a response object. The `failed` status appears in streams, tasks and stored responses.

`GET /lmsp/v1/responses/{response_id}` returns a response that was stored with `store: true`. It is optional and needs the `conversations` capability. An unknown id gives `404` with `response_not_found`.

## 6.3 Streaming {#streaming}

To stream, set `stream` in the request:

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

The service answers `200 OK` with `Content-Type: text/event-stream` and sends semantic events. Each event has an `id`, an `event` name and a JSON `data` object:

```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}}
```

At least these events are defined:

| Event | Meaning | Data fields |
| --- | --- | --- |
| `response.created` | The response was created. | `response_id`, `status` (`in_progress`) |
| `response.output_item.added` | A new output item started. | `response_id`, `output_index`, `item` |
| `response.output_text.delta` | Text was appended to a message item. | `response_id`, `item_id`, `delta` |
| `response.output_item.done` | An output item is complete. | `response_id`, `output_index`, `item` |
| `response.completed` | The response finished. Final event. | `response_id`, `status`, `usage` |
| `response.incomplete` | Generation stopped early, for example at the token limit. Final event. | `response_id`, `status`, `incomplete_details`, `usage` |
| `response.failed` | Generation failed. Final event. | `response_id`, `status`, `error` |
| `response.cancelled` | The response was cancelled. Final event. | `response_id`, `status` |
| `error` | A problem that is not tied to one response, after which the stream cannot continue. | `error`, a problem object |

A service closes the stream after a final event or an `error` event.

Services **MAY** send additional events, for example for tool-call arguments, images, audio and structured JSON deltas. Clients **MUST** ignore event types they do not know.

If the client closes the connection of a streaming request, the service **SHOULD** stop generating.

## 6.4 Event ids and reconnection {#reconnection}

The `id` of an event **MUST** be an integer sequence number that is strictly increasing within the response, starting at 1.

After a dropped connection, a client resumes by sending the last id it received. It does this with a header:

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

or with a query parameter:

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

Both go to the events endpoint of a task ([section 7.4](https://madeforagent.com/lmsp/1.0/tasks.md#events)). The service replays only the events whose sequence number is greater than 2. If both are present, `after` wins.

If the requested events are older than the retention period, the service answers `410 Gone` with the error `event_history_expired`. The client then fetches the final result instead, with `GET /lmsp/v1/tasks/{task_id}`.

A response streamed directly from `POST /lmsp/v1/responses` belongs to that HTTP connection and cannot be resumed. A client that needs resumable output **SHOULD** create a [task](https://madeforagent.com/lmsp/1.0/tasks.md) and follow its events.

## 6.5 Tool calls {#tools}

LMSP 1.0 defines function tools that the client executes. The service does not run tools itself. The `tools:execute` scope is reserved for extensions that do ([section 10.3](https://madeforagent.com/lmsp/1.0/security.md#scopes)).

First, the client lists the tools. Each entry has `type` set to `function`, a `name`, a `description` and `parameters`, a JSON Schema object. `tool_choice` is `auto` (the default), `none`, `required`, or `{"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"
}
```

When the model decides to call a tool, the response contains a `function_call` item. Its `arguments` are a JSON-encoded string. In a stream, the item arrives complete in `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
}
```

The client runs the tool, then sends a new request that carries the full context: the earlier input items, the `function_call` item as received, and a `function_call_output` item with the same `call_id`. Its `output` is a string, usually JSON-encoded.

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