Responses and streaming
- Normative
- Draft 1.0
- Updated
- View as Markdown
6.1 Request
POST /lmsp/v1/responses
Authorization: Bearer <token>
Content-Type: application/json
Idempotency-Key: 01J8M5T7Z2K4N6Q8S0V3X5B9DG
LMSP-Version: 1.0
{
"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). |
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). 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). |
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
When stream is false, the service returns 200 OK with a response object after generation has finished.
{
"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) 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), 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
To stream, set stream in the request:
{
"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:
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
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:
Last-Event-ID: 2
or with a query parameter:
GET /lmsp/v1/tasks/{task_id}/events?after=2
Both go to the events endpoint of a task (section 7.4). 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 and follow its events.
6.5 Tool calls
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).
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"}.
{
"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.
{
"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.
{
"type": "function_call_output",
"call_id": "call_01J8M7B3N9T2V5X8Z1C4D7F0HM",
"output": "{\"temperature_c\":17,\"condition\":\"light rain\"}"
}