Streaming
Recibe la salida de Claude, GPT y Gemini en streaming con el SSE nativo de cada fabricante. Fallos tempranos ocultos, cortes visibles y coste al cancelar.
El streaming funciona como lo diseñó cada fabricante: server-sent events en su propio formato, que te llegan byte a byte. El gateway solo los lee para contar el uso y para protegerte de canales que fallan. Tu código actual para procesar streams no necesita cambios.
Activar el streaming#
| API | Cómo pedir un stream | Último evento |
|---|---|---|
| Anthropic Messages | "stream": true en el cuerpo | event: message_stop |
| OpenAI Chat Completions | "stream": true en el cuerpo | data: [DONE] |
| Gemini | POST /v1beta/models/{model}:streamGenerateContent?alt=sse | Un evento cuyo candidato trae finishReason |
El código con SDK de cada uno está en las guías: Anthropic, OpenAI, Gemini.
El primer token se retiene y los fallos previos se reintentan#
A veces un canal acepta una petición y se rinde antes de producir nada, por ejemplo enviando message_start y después un error de sobrecarga. Para ocultarlo, el gateway retiene el inicio de cada stream hasta que llega el primer contenido real (texto, thinking o una llamada a función). Un canal que falla antes de ese punto no te ha enviado nada, así que el gateway reenvía la petición a otro canal sin hacer ruido y recibes un único stream limpio desde el principio.
Los canales lentos en arrancar reciben el mismo trato. En modelos Claude sin pensamiento extendido, un canal que no ha producido ningún token tras 10 segundos (más 1 segundo por cada 10K tokens de prompt) se abandona por otro, salvo que sea el último disponible. Con thinking activado, el modelo puede pensar todo lo que necesite. Las llamadas sin streaming siguen la misma regla.
Para tu cliente, esto significa:
- La línea de estado y las cabeceras llegan con el primer token: el tiempo hasta el primer byte es el tiempo hasta el primer token.
- Si todos los canales fallan antes del primer token, recibes un error HTTP normal en el formato de tu API, no un stream roto.
- La retención dura como mucho 15 segundos. Un modelo que piensa en silencio más tiempo empieza a emitir en ese momento, y desde ahí la petición queda ligada a su canal.
- Dale a la respuesta al menos 30 segundos para empezar. Una petición también puede esperar unos segundos en cola por capacidad; consulta Límites y conmutación por error.
Ejemplos de streams#
event: message_start
data: {"type":"message_start","message":{"id":"msg_01Qe4n","type":"message","role":"assistant","model":"claude-sonnet-5-5","content":[],"stop_reason":null,"usage":{"input_tokens":12,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":1}}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Red, green, blue."}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":7}}
event: message_stop
data: {"type":"message_stop"}Cuando un stream en curso se rompe#
Tras el primer token, la petición pertenece a su canal y ya no se puede mover. Aún hay dos cosas que pueden cortarla.
Silencio. Si el canal no envía nada durante 3 minutos, el gateway cierra el stream con un evento de error en el formato de tu API:
event: error
data: {"type":"error","error":{"type":"api_error","message":"Upstream stopped responding mid-stream"}}Un error lanzado por el canal, como un modelo sobrecargado. Recibes un evento de error neutro en tu formato en lugar del texto propio del canal: overloaded_error (OpenAI rate_limit_exceeded, Gemini RESOURCE_EXHAUSTED) si el canal está ocupado; si no, api_error (Gemini UNAVAILABLE) con el mensaje The model provider returned an error. Please retry.
En ambos casos el stream termina sin su evento de cierre habitual (message_stop, data: [DONE] o un finishReason). Trata como incompleto cualquier stream que no lo tenga: conserva o descarta el texto parcial y repite la petición entera si necesitas la respuesta completa. Los SDK de Anthropic y OpenAI lanzan una excepción al recibir un evento de error.
Detener un stream antes de tiempo#
Cierra la conexión para dejar de generar: sal del bucle o llama a stream.close() / controller.abort(). El gateway detiene el modelo y cobra, a mitad del precio de lista, solo lo producido hasta ese momento:
- Entrada: el recuento que el stream ya informó (Anthropic lo envía al principio) o, si no informó nada, alrededor de un token por cada cuatro caracteres de tu petición.
- Salida: el mayor entre el recuento informado hasta entonces y alrededor de un token por cada cuatro caracteres del texto que ya te llegó. Los streams que terminan antes de tiempo rara vez informan un recuento final de salida, así que lo habitual es que se aplique la estimación.
Un stream cerrado por un corte se factura igual. Cada stream cancelado o cortado aparece en Registros con su cargo. Consulta también Modelos y precios.