Tokia

Streaming (SSE)

Quando você passa "stream": true na chamada de chat/completions, o Tokia retorna a resposta em tempo real via Server-Sent Events (SSE), token por token. Isso permite exibir texto na tela enquanto o modelo ainda está gerando — a experiência de “digitação ao vivo” que todo chat de IA usa.

Como funciona

Em vez de esperar a resposta completa (que pode levar segundos), o servidor envia pequenos chunks assim que cada token fica pronto. Cada chunk é um evento SSE no formato:

text
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Olá"},"finish_reason":null}]}

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"!"},"finish_reason":null}]}

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: [DONE]
  • Cada linha data: contém um JSON com o delta (trecho novo)
  • delta.content tem o texto parcial do token gerado
  • Quando finish_reason vira "stop", a geração terminou
  • A linha data: [DONE] indica fim do stream

Python

python
from openai import OpenAI

client = OpenAI(
    api_key="sk-...",
    base_url="https://api.usetokia.com/v1",
)

stream = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Conte uma piada curta"}],
    stream=True,
)

for chunk in stream:
    content = chunk.choices[0].delta.content
    if content:
        print(content, end="", flush=True)

print()  # nova linha ao final

Python async

python
import asyncio
from openai import AsyncOpenAI

client = AsyncOpenAI(
    api_key="sk-...",
    base_url="https://api.usetokia.com/v1",
)

async def main():
    stream = await client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": "Conte uma piada curta"}],
        stream=True,
    )

    async for chunk in stream:
        content = chunk.choices[0].delta.content
        if content:
            print(content, end="", flush=True)

asyncio.run(main())

TypeScript / Node.js

typescript
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.TOKIA_KEY,
  baseURL: "https://api.usetokia.com/v1",
});

const stream = await client.chat.completions.create({
  model: "gpt-4o-mini",
  messages: [{ role: "user", content: "Conte uma piada curta" }],
  stream: true,
});

for await (const chunk of stream) {
  const content = chunk.choices[0]?.delta?.content;
  if (content) {
    process.stdout.write(content);
  }
}

No browser (fetch + ReadableStream)

typescript
const response = await fetch("https://api.usetokia.com/v1/chat/completions", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk-...",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "gpt-4o-mini",
    messages: [{ role: "user", content: "Olá!" }],
    stream: true,
  }),
});

const reader = response.body!.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  const text = decoder.decode(value, { stream: true });
  for (const line of text.split("\n")) {
    if (!line.startsWith("data: ") || line === "data: [DONE]") continue;
    const json = JSON.parse(line.slice(6));
    const content = json.choices?.[0]?.delta?.content;
    if (content) {
      document.getElementById("output")!.textContent += content;
    }
  }
}

cURL

bash
curl -N https://api.usetokia.com/v1/chat/completions \
  -H "Authorization: Bearer $TOKIA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o-mini",
    "messages": [{"role": "user", "content": "Olá!"}],
    "stream": true
  }'

O flag -N desabilita o buffer do cURL, fazendo os tokens aparecerem em tempo real no terminal.

stream_options: uso e tokens

Para receber o total de tokens consumidos no final do stream, passe stream_options:

json
{
  "model": "gpt-4o-mini",
  "messages": [{"role": "user", "content": "Olá"}],
  "stream": true,
  "stream_options": {
    "include_usage": true
  }
}

O último chunk antes de [DONE] terá o campo usage com prompt_tokens, completion_tokens e total_tokens.

Tratamento de erros em streaming

Erros podem acontecer antes do stream começar (HTTP 4xx/5xx normal) ou durante o stream (conexão cortada). Trate ambos:

python
from openai import OpenAI, APIError

client = OpenAI(api_key="sk-...", base_url="https://api.usetokia.com/v1")

try:
    stream = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": "Olá"}],
        stream=True,
    )
    for chunk in stream:
        content = chunk.choices[0].delta.content
        if content:
            print(content, end="", flush=True)
except APIError as e:
    print(f"\nErro da API: {e.status_code} - {e.message}")
except Exception as e:
    print(f"\nErro de conexão: {e}")

Quando usar streaming

  • Use streaming quando o resultado é exibido ao usuário em tempo real (chatbots, copilots, assistentes)
  • Não use streaming quando você precisa do resultado completo antes de processar (classificação, extração de dados, pipelines batch)
  • Streaming não altera o preço — o custo por token é o mesmo
  • O timeout é mais longo em streaming (o servidor aguarda enquanto tokens estão sendo gerados)

Próximos passos