For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
Navegação principal
Theme

Modo WebSocket

Use uma única conexão WebSocket persistente para executar conversas em paralelo, criar forks de cadeias de respostas e enviar entradas incrementais para reduzir a latência em fluxos de trabalho com agentes de IA.

A API Responses oferece um modo WebSocket para fluxos de trabalho de longa duração com muitas chamadas de ferramentas. Além de reduzir a latência, esse modo permite usar stream_id para multiplexar conversas via WebSocket. Com uma única conexão persistente a /v1/responses, você pode executar conversas em paralelo e criar um fork de uma conversa existente em um novo fluxo. Para dar continuidade à conversa a cada turno, envie apenas os novos itens de entrada junto com previous_response_id.

O modo WebSocket é compatível tanto com zero retenção de dados (ZDR) quanto com store=false.

Por que usar o modo WebSocket

O modo WebSocket é especialmente útil quando um fluxo de trabalho exige muitas trocas entre o modelo e as ferramentas, como na programação agêntica ou em ciclos de orquestração com chamadas repetidas de ferramentas.

Como a conexão permanece aberta e cada turno envia apenas as novas entradas, o modo WebSocket reduz o custo de dar continuidade a cada turno e diminui a latência de ponta a ponta em cadeias longas. Em execuções com 20 ou mais chamadas de ferramentas, observamos ganhos de velocidade de até cerca de 40% de ponta a ponta.

Conectar e criar respostas

Instale as dependências do WebSocket com pip install "openai[realtime]>=3.8.0" para Python, npm install openai@^7.10.0 ws para JavaScript ou gem install openai async-websocket para Ruby.

Para Go, execute go get github.com/openai/openai-go/v3@v3.98.0. Para Java, adicione a dependência Maven com.openai:openai-java:4.101.0 e use o Java 17 ou posterior nestes exemplos. Essas versões dos SDKs de Go e Java oferecem suporte nativo ao WebSocket da API Responses.

No modo WebSocket, inicie cada turno enviando um evento response.create a partir do cliente. O conteúdo do evento segue o formato habitual do corpo da requisição de criação de respostas, com a diferença de que campos específicos do transporte, como stream e background, não são usados.

import OpenAI from "openai";
import { ResponsesWS } from "openai/resources/responses/ws";

const client = new OpenAI();

const ws = new ResponsesWS(client);
try {
  ws.send({
    type: "response.create",
    stream_id: "main",
    model: "gpt-6-astra",
    store: false,
    input: [
      {
        type: "message",
        role: "user",
        content: [{ type: "input_text", text: "Find fizz_buzz()" }],
      },
    ],
    tools: [],
  });
  let completed = false;
  for await (const event of ws) {
    if (event.type === "error") throw event.error;
    if (event.type !== "message") continue;
    const message = event.message;
    if (message.type === "response.output_text.delta") {
      process.stdout.write(message.delta);
    } else if (message.type === "response.completed") {
      completed = true;
      break;
    } else if (
      message.type === "response.failed" ||
      message.type === "response.incomplete"
    ) {
      throw new Error(JSON.stringify(message));
    }
  }
  if (!completed)
    throw new Error("Connection closed before the response finished.");
} finally {
  ws.close();
}

Os clientes podem preparar o estado da requisição com antecedência enviando response.create com generate: false. Isso é útil quando você já sabe quais ferramentas, instruções ou mensagens personalizadas pretende enviar em um dos próximos turnos. Com generate: false, a requisição não retorna uma saída do modelo, mas prepara o estado para que a geração no próximo turno comece mais rápido. Essa requisição preparatória retorna um ID de resposta que você pode usar em previous_response_id para continuar a cadeia, inclusive em turnos posteriores. A próxima seção explica como continuar uma sessão usando previous_response_id e entradas incrementais.

Continuar com entradas incrementais

Para adicionar instruções do usuário enquanto uma resposta está sendo gerada, use o direcionamento durante o turno. Esse recurso preserva o trabalho já concluído e incorpora as novas instruções à continuação. Use o padrão de response.create a seguir para continuar a conversa entre turnos ou enviar resultados de ferramentas.

Para continuar uma execução, envie outro evento response.create com os seguintes campos:

  • previous_response_id definido como o ID da resposta anterior.
  • input contendo apenas os novos itens (por exemplo, as saídas das ferramentas e a próxima mensagem do usuário).
import OpenAI from "openai";
import { ResponsesWS } from "openai/resources/responses/ws";

const client = new OpenAI();
const model = "gpt-6-astra";

const tools = [
  {
    type: "function",
    name: "get_test_results",
    description: "Return a local demo test result.",
    parameters: { type: "object", properties: {}, additionalProperties: false },
    strict: true,
  },
];

async function waitForResponse(ws) {
  for await (const event of ws) {
    if (event.type === "error") throw event.error;
    if (event.type !== "message") continue;
    const message = event.message;
    if (message.type === "response.output_text.delta") {
      process.stdout.write(message.delta);
    } else if (message.type === "response.completed") {
      return message.response;
    } else if (
      message.type === "response.failed" ||
      message.type === "response.incomplete"
    ) {
      throw new Error(JSON.stringify(message));
    }
  }
  throw new Error("Connection closed before the response finished.");
}

const ws = new ResponsesWS(client);
try {
  ws.send({
    type: "response.create",
    stream_id: "main",
    model,
    store: false,
    input: "Find the failing test and suggest a fix.",
    tools,
    tool_choice: { type: "function", name: "get_test_results" },
    parallel_tool_calls: false,
  });
  const first = await waitForResponse(ws);
  const call = first.output.find((item) => item.type === "function_call");
  if (!call || call.name !== "get_test_results") {
    throw new Error("Expected a get_test_results function call.");
  }
  const result = {
    test: "test_fizz_buzz",
    failure: 'Expected "FizzBuzz" for 15, got "Fizz".',
  };

  // Continue on the same socket with the actual response and tool-call IDs.
  ws.send({
    type: "response.create",
    stream_id: "main",
    model,
    store: false,
    previous_response_id: first.id,
    input: [
      {
        type: "function_call_output",
        call_id: call.call_id,
        output: JSON.stringify(result),
      },
      { role: "user", content: "Now optimize it." },
    ],
    tools,
    tool_choice: "none",
  });
  await waitForResponse(ws);
} finally {
  ws.close();
}

Como funciona a continuação

O modo WebSocket segue as mesmas regras do modo HTTP para encadear respostas com previous_response_id, mas permite continuar a conversa com menor latência pelo socket ativo.

Enquanto a conexão WebSocket está ativa, o serviço mantém o estado recente das respostas anteriores em um cache em memória próprio da conexão. Quando você usa stream_id, cada fluxo mantém sua resposta mais recente no cache. Continuar a partir dessa resposta é rápido porque o serviço pode reutilizar o estado mantido na conexão. Como o serviço mantém o estado das respostas anteriores apenas na memória, sem gravá-lo em disco, você pode usar o modo WebSocket de forma compatível com store=false e com zero retenção de dados (ZDR).

Se um previous_response_id não estiver no cache em memória, o comportamento dependerá de você ter optado por armazenar as respostas:

  • Com store=true, o serviço pode recuperar o estado de respostas mais antigas pelos respectivos IDs quando esse estado estiver disponível no armazenamento persistente. A continuação ainda pode funcionar, mas perde a vantagem de latência do cache em memória.
  • Com store=false (inclusive com ZDR), não há uma cópia persistente à qual recorrer. Se o ID não estiver no cache, a requisição retornará previous_response_not_found.

Se uma continuação no mesmo fluxo retornar um erro 4xx ou 5xx, o serviço removerá do cache da conexão o previous_response_id referenciado. Se um fork para outro fluxo retornar um erro, o serviço preservará a resposta de origem compartilhada para que o fluxo original possa continuar.

Compactação e criação de novas respostas

Se você usa compactação, há duas formas de continuar:

Compactação no servidor (context_management)

Quando você habilita a compactação no servidor (context_management com compact_threshold), a compactação ocorre durante a geração normal de respostas em /responses. No modo WebSocket, continue como de costume. Envie o próximo evento response.create com o previous_response_id mais recente e apenas os novos itens de entrada.

Uso independente de /responses/compact

O endpoint /responses/compact retorna uma nova janela de entrada compactada em vez de um ID de resposta. Após a compactação, crie uma nova resposta na conexão WebSocket usando a janela compactada como input e acrescentando os próximos itens do usuário ou das ferramentas.

Para iniciar uma nova cadeia, omita previous_response_id ou defina seu valor como null. Envie a saída compactada como a recebeu, sem remover itens da janela retornada.

import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";

// Compact your current window with an HTTP request.
const compacted = await client.responses.compact({
  model: "gpt-6-astra",
  input: longInputItems,
});
const nextInput = toResponseInputItems(compacted.output);
nextInput.push({
  type: "message",
  role: "user",
  content: [{ type: "input_text", text: "Continue from here." }],
});

// Start a new response on the WebSocket using the compacted window.
const ws = new ResponsesWS(client);
try {
  ws.send({
    type: "response.create",
    stream_id: "main",
    model: "gpt-6-astra",
    store: false,
    input: nextInput,
    tools: [],
  });
  let completed = false;
  for await (const event of ws) {
    if (event.type === "error") throw event.error;
    if (event.type !== "message") continue;
    const message = event.message;
    if (message.type === "response.output_text.delta") {
      process.stdout.write(message.delta);
    } else if (message.type === "response.completed") {
      completed = true;
      break;
    } else if (
      message.type === "response.failed" ||
      message.type === "response.incomplete"
    ) {
      throw new Error(JSON.stringify(message));
    }
  }
  if (!completed)
    throw new Error("Connection closed before the response finished.");
} finally {
  ws.close();
}

Executar conversas em paralelo

Você pode manter conversas em paralelo na mesma conexão usando o parâmetro stream_id. Envie eventos response.create independentes, um após o outro, com valores diferentes de stream_id. O servidor pode executá-los simultaneamente em uma única conexão. Como os eventos podem chegar intercalados, mantenha um único laço de leitura e encaminhe cada evento de acordo com seu stream_id.

Um stream_id identifica um fluxo ordenado dentro de uma conexão WebSocket. Distinga as funções de stream_id e previous_response_id:

  • stream_id determina o destino dos eventos e quais requisições são executadas em ordem de chegada.
  • previous_response_id determina a partir de qual resposta anterior a conversa continua.

Essa separação permite usar a mesma conexão de duas maneiras.

uma conexão WebSocket
├─ stream_id="planner"   elabore um plano de implantação
└─ stream_id="research"  liste os riscos da implantação

Requisições com o mesmo stream_id são executadas em ordem de chegada, uma por vez. Requisições com valores diferentes de stream_id podem ser executadas simultaneamente.

Limites por conexão

  • Uma conexão pode ter até 16 respostas em andamento no total, contando os fluxos nomeados e o fluxo padrão. A conexão aceita novos eventos response.create e os mantém na fila até que uma resposta em andamento seja concluída.
  • Uma conexão aceita até 32 valores distintos de stream_id para fluxos nomeados. O fluxo padrão implícito não conta para esse limite. Ao atingir o limite, reutilize um stream_id existente ou abra uma nova conexão.

Criar um fork de uma conversa em um novo fluxo

Para criar uma ramificação a partir de uma resposta concluída, envie o ID dessa resposta em previous_response_id junto com um novo stream_id. Enquanto a resposta estiver disponível, o novo fluxo herdará seu contexto e o fluxo original poderá continuar. Depois que o fork começar, as duas ramificações poderão ser executadas simultaneamente porque usam IDs de fluxo diferentes.

Com store=false (inclusive com ZDR), um fork para outro fluxo depende de a resposta de origem permanecer no cache da conexão. Se o fluxo original avançar ou falhar enquanto o fork estiver na fila, o serviço poderá remover essa resposta do cache antes que o fork comece. Nesse caso, o fork retornará previous_response_not_found. Aguarde até que o novo fluxo emita response.in_progress antes de avançar o fluxo original, ou tente novamente com previous_response_id definido como null e reenvie todo o contexto de entrada.

main:   resp_1 ──▶ resp_2 ──▶ resp_3
                       ╲
critic:                 resp_4 ──▶ resp_5

Reutilizar um stream_id sem previous_response_id inicia uma nova resposta sem dar continuidade à conversa.

Veja as principais chamadas:

# Um socket, duas conversas independentes.
send_create(connection, "planner", "Elabore um plano de implantação.")
send_create(connection, "research", "Liste os riscos da implantação.")

# Crie um fork da resposta do fluxo planner e continue a ramificação original em paralelo.
send_create(
    connection,
    "critic",
    "Identifique lacunas neste plano.",
    previous_response_id=planner_response_id,
)
wait_for_in_progress(connection, "critic")
send_create(
    connection,
    "planner",
    "Adicione as etapas de reversão da implantação.",
    previous_response_id=planner_response_id,
)

Exemplo completo

Execute conversas em paralelo e crie um fork de uma delas
import OpenAI from "openai";
import { ResponsesWS } from "openai/resources/responses/ws";

const client = new OpenAI();

const latestResponseIdByLane = new Map();

function sendCreate(
  ws,
  streamId,
  text,
  previousResponseId = latestResponseIdByLane.get(streamId)
) {
  ws.send({
    type: "response.create",
    stream_id: streamId,
    model: "gpt-6-astra",
    store: false,
    input: [
      {
        type: "message",
        role: "user",
        content: [{ type: "input_text", text }],
      },
    ],
    previous_response_id: previousResponseId,
  });
}

async function readMessage(events) {
  while (true) {
    const { value: event, done } = await events.next();
    if (done)
      throw new Error("Connection closed before all responses finished.");
    if (event.type === "error") throw event.error;
    if (event.type !== "message") continue;
    const message = event.message;
    if (
      message.type === "response.failed" ||
      message.type === "response.incomplete"
    ) {
      throw new Error(
        `Lane ${message.stream_id} failed: ${JSON.stringify(message)}`
      );
    }
    return message;
  }
}

async function drainUntilComplete(events, expectedStreamIds) {
  const remaining = new Set(expectedStreamIds);
  while (remaining.size > 0) {
    const message = await readMessage(events);
    const streamId = message.stream_id;
    if (!streamId || !remaining.has(streamId)) continue;
    if (message.type === "response.completed") {
      latestResponseIdByLane.set(streamId, message.response.id);
      remaining.delete(streamId);
    }
  }
}

async function waitForInProgress(events, streamId) {
  while (true) {
    const message = await readMessage(events);
    if (
      message.type === "response.in_progress" &&
      message.stream_id === streamId
    )
      return;
  }
}

const ws = new ResponsesWS(client);
// Keep one iterator so events stay queued while moving between phases.
const events = ws.stream();
try {
  // Run two independent conversations in parallel.
  sendCreate(
    ws,
    "planner",
    "Draft a deployment plan for a stateless API service."
  );
  sendCreate(
    ws,
    "research",
    "List common deployment risks for a stateless API service."
  );
  await drainUntilComplete(events, new Set(["planner", "research"]));

  // Fork the planner conversation and continue its original branch in parallel.
  const plannerResponseId = latestResponseIdByLane.get("planner");
  sendCreate(
    ws,
    "critic",
    "Find gaps in this deployment plan.",
    plannerResponseId
  );
  // Let the fork load its parent before advancing the original lane's cache.
  await waitForInProgress(events, "critic");
  sendCreate(
    ws,
    "planner",
    "Add rollback and monitoring steps to the plan.",
    plannerResponseId
  );
  await drainUntilComplete(events, new Set(["critic", "planner"]));
} finally {
  await events.return?.();
  ws.close();
}

Um stream_id deve ter de 1 a 256 caracteres e pode conter apenas letras, números, sublinhados (_), hífens (-) e pontos (.). Use esse campo apenas em eventos response.create via WebSocket. Não o inclua em requisições HTTP POST /v1/responses.

Nos fluxos nomeados, os eventos do servidor incluem o stream_id correspondente, inclusive os eventos finais e os erros específicos de uma requisição.

Se você omitir stream_id, a requisição usará um fluxo padrão implícito e seus eventos não incluirão stream_id. Nos demais aspectos, o fluxo padrão segue as mesmas regras de ordenação e concorrência dos fluxos nomeados. Uma string vazia não é um stream_id válido. Para selecionar o fluxo padrão, omita o campo.

Comportamento e limites da conexão

  • Os eventos de cada resposta seguem o modelo de eventos de streaming da API Responses. Eventos de fluxos diferentes podem chegar intercalados.
  • Requisições com o mesmo stream_id são executadas em ordem de chegada, uma por vez. Requisições em fluxos diferentes podem ser executadas simultaneamente.
  • As conexões duram até 60 minutos. Reconecte-se ao atingir esse limite.

Reconectar e recuperar os fluxos

Quando uma conexão é encerrada ou atinge o limite de 60 minutos, todos os fluxos perdem o cache daquela conexão. Abra uma nova conexão WebSocket e recupere cada fluxo de uma das seguintes formas:

  1. Se você armazenou uma resposta anterior (store=true) e tem um ID de resposta válido, continue o fluxo com previous_response_id e os novos itens de entrada.
  2. Se não for possível continuar um fluxo (por exemplo, ao usar store=false ou ZDR, ou ao receber previous_response_not_found), inicie uma nova resposta definindo previous_response_id como null (ou omitindo o campo) e envie todo o contexto de entrada para o próximo turno desse fluxo.
  3. Se você compactou o contexto com /responses/compact, use a janela compactada retornada como base do input da nova resposta e acrescente os itens mais recentes do usuário ou das ferramentas.

Erros a tratar

Quando o servidor consegue associar um erro a um fluxo nomeado, o evento de erro inclui stream_id. Os demais fluxos podem continuar após um erro específico de uma requisição.

previous_response_not_found

{
  "type": "error",
  "status": 400,
  "stream_id": "main",
  "error": {
    "type": "invalid_request_error",
    "code": "previous_response_not_found",
    "message": "Previous response with id 'resp_abc' not found.",
    "param": "previous_response_id"
  }
}

invalid_stream_id

{
  "type": "error",
  "status": 400,
  "error": {
    "type": "invalid_request_error",
    "code": "invalid_stream_id",
    "message": "The 'stream_id' field must be a non-empty string with at most 256 characters and may only contain letters, numbers, underscores, hyphens, and periods.",
    "param": "stream_id"
  }
}

websocket_stream_limit_reached

{
  "type": "error",
  "status": 400,
  "stream_id": "agent_33",
  "error": {
    "type": "invalid_request_error",
    "code": "websocket_stream_limit_reached",
    "message": "This WebSocket connection has reached its maximum number of distinct stream IDs (32). Reuse an existing stream_id or open a new WebSocket connection.",
    "param": "stream_id"
  }
}

websocket_connection_limit_reached

{
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "code": "websocket_connection_limit_reached",
    "message": "Responses websocket connection limit reached (60 minutes). Create a new websocket connection to continue."
  },
  "status": 400
}

Guias relacionados