Respostas e streaming

6.1 Requisição

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
}

O protocolo principal usa uma estrutura input e output no estilo Responses, em vez de uma lista simples de strings de mensagem, para que um único formato de requisição possa carregar:

  • mensagens de vários turnos;
  • texto, imagens, áudio e arquivos;
  • chamadas de ferramentas e resultados de ferramentas;
  • saída estruturada;
  • resumos de raciocínio;
  • saída multimodal.

O LMSP 1.0 define apenas conteúdo de texto. Os tipos de conteúdo de imagem, áudio e arquivo ficam reservados para versões futuras.

Campo Obrigatório Significado
model Sim ID do modelo a usar.
input Sim Array de itens de entrada. Um item message tem um role (system, user ou assistant) e um array content de partes, como input_text. O uso de ferramentas acrescenta itens function_call e function_call_output (seção 6.5).
stream Não true para receber eventos enviados pelo servidor. O padrão é false.
max_output_tokens Não Limite superior do número de tokens gerados.
temperature Não Temperatura de amostragem.
response_format Não {"type": "text"} (o padrão) ou {"type": "json_schema", "json_schema": {"name": "...", "schema": {...}}} para saída estruturada. Exige a capacidade structured_output.
tools, tool_choice Não Ferramentas de função que o modelo pode chamar (seção 6.5). Exige a capacidade tools.
store Não Pede ao serviço que guarde a resposta, para que ela possa ser recuperada depois e usada como previous_response_id. O padrão é false. Ignorado por serviços sem a capacidade conversations.
previous_response_id Não Continua a partir de uma resposta armazenada. Exige a capacidade conversations.
extensions Não Extensões privadas (seção 9.1).

Uma requisição que exige uma capacidade que o modelo não tem é rejeitada com 400 e unsupported_capability. Uma requisição que não cabe na janela de contexto é rejeitada com 400 e context_length_exceeded.

Um serviço que consegue armazenar conversas PODE também suportar previous_response_id. Caso contrário, toda requisição DEVE carregar o contexto completo. Um serviço NÃO DEVE depender da conexão HTTP para manter o estado da conversa.

6.2 Resposta síncrona

Quando stream é false, o serviço retorna 200 OK com um objeto de resposta depois que a geração termina.

{
  "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
}
Campo Significado
id Identificador da resposta, como resp_....
object Sempre "response".
created_at Momento da criação, em segundos desde a época Unix.
status in_progress, completed, incomplete, failed ou cancelled. Uma resposta síncrona é retornada em um status final.
model ID do modelo que produziu a resposta.
output Array de itens de saída.
usage Contagens de tokens: input_tokens, output_tokens e total_tokens.
error null ou um objeto de problema (seção 9.3) quando status é failed.
incomplete_details null ou um objeto com um reason, como max_output_tokens, quando status é incomplete.

Tipos de itens de saída:

Tipo de item Formato
message id, role (assistant) e content, um array de partes output_text que têm, cada uma, um text.
function_call id, call_id, name e arguments, uma string codificada em JSON.
reasoning id e summary, um array de partes summary_text que têm, cada uma, um text.

Se a geração falhar, uma requisição síncrona é respondida com um status de erro e um documento de problema (seção 9.3), e não com um objeto de resposta. O status failed aparece em streams, tarefas e respostas armazenadas.

GET /lmsp/v1/responses/{response_id} retorna uma resposta que foi armazenada com store: true. É opcional e exige a capacidade conversations. Um ID desconhecido resulta em 404 com response_not_found.

6.3 Streaming

Para fazer streaming, defina stream na requisição:

{
  "stream": true
}

O serviço responde 200 OK com Content-Type: text/event-stream e envia eventos semânticos. Cada evento tem um id, um nome event e um objeto JSON data:

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

Pelo menos estes eventos são definidos:

Evento Significado Campos de dados
response.created A resposta foi criada. response_id, status (in_progress)
response.output_item.added Um novo item de saída começou. response_id, output_index, item
response.output_text.delta Texto foi acrescentado a um item de mensagem. response_id, item_id, delta
response.output_item.done Um item de saída está completo. response_id, output_index, item
response.completed A resposta terminou. Evento final. response_id, status, usage
response.incomplete A geração parou antes do fim, por exemplo no limite de tokens. Evento final. response_id, status, incomplete_details, usage
response.failed A geração falhou. Evento final. response_id, status, error
response.cancelled A resposta foi cancelada. Evento final. response_id, status
error Um problema que não está ligado a uma única resposta, após o qual o fluxo não pode continuar. error, um objeto de problema

Um serviço fecha o fluxo após um evento final ou um evento error.

Os serviços PODEM enviar eventos adicionais, por exemplo para argumentos de chamadas de ferramentas, imagens, áudio e deltas de JSON estruturado. Os clientes DEVEM ignorar os tipos de evento que não conhecem.

Se o cliente fechar a conexão de uma requisição em streaming, o serviço DEVERIA parar de gerar.

6.4 IDs de evento e reconexão

O id de um evento DEVE ser um número de sequência inteiro, estritamente crescente dentro da resposta, começando em 1.

Após uma conexão interrompida, um cliente retoma enviando o último ID que recebeu. Ele faz isso com um cabeçalho:

Last-Event-ID: 2

ou com um parâmetro de consulta:

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

Ambos vão para o endpoint de eventos de uma tarefa (seção 7.4). O serviço reenvia apenas os eventos cujo número de sequência é maior que 2. Se ambos estiverem presentes, after prevalece.

Se os eventos solicitados forem mais antigos que o período de retenção, o serviço responde 410 Gone com o erro event_history_expired. O cliente então busca o resultado final, com GET /lmsp/v1/tasks/{task_id}.

Uma resposta enviada em streaming diretamente por POST /lmsp/v1/responses pertence àquela conexão HTTP e não pode ser retomada. Um cliente que precisa de saída retomável DEVERIA criar uma tarefa e acompanhar os seus eventos.

6.5 Chamadas de ferramentas

O LMSP 1.0 define ferramentas de função que o cliente executa. O serviço não executa ferramentas por conta própria. O escopo tools:execute é reservado para extensões que o fazem (seção 10.3).

Primeiro, o cliente lista as ferramentas. Cada entrada tem type definido como function, um name, uma description e parameters, um objeto JSON Schema. tool_choice é auto (o padrão), none, required ou {"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"
}

Quando o modelo decide chamar uma ferramenta, a resposta contém um item function_call. Seus arguments são uma string codificada em JSON. Em um stream, o item chega completo em 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
}

O cliente executa a ferramenta e então envia uma nova requisição que carrega o contexto completo: os itens de entrada anteriores, o item function_call como foi recebido e um item function_call_output com o mesmo call_id. Seu output é uma string, geralmente codificada em JSON.

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