Tratamento de chat
Clientes de chat que usam inferência por streaming devem tratar a resposta como uma linha do tempo de eventos, não como blocos separados por tipo de informação. Em uma única inferência, o modelo pode raciocinar, chamar ferramentas do lado do servidor, responder parcialmente, voltar a raciocinar e continuar a resposta. Essa sequência deve ser preservada na interface.
O comportamento mais importante é: cada pedaço recebido pelo SSE representa o próximo passo da resposta da assistente. O cliente deve construir a resposta na ordem em que os eventos chegam.
Por que manter uma linha do tempo
Modelos diferentes organizam a resposta de formas diferentes. O diagrama de sequência abaixo representa dois comportamentos comuns dentro de uma única inferência:
sequenceDiagram
participant Usuario
participant Cliente
participant AIVAX
participant Modelo
participant Ferramentas
Usuario->>Cliente: Envia mensagem
Cliente->>AIVAX: Cria inferencia em streaming
AIVAX->>Modelo: Inicia inferencia
Note over AIVAX,Cliente: Padrao comum em modelos como Gemini
AIVAX-->>Cliente: reasoning
AIVAX->>Ferramentas: Chamada de ferramenta
Ferramentas-->>AIVAX: Resultado
AIVAX-->>Cliente: tool call ou servertool
AIVAX-->>Cliente: answer
AIVAX-->>Cliente: reasoning
AIVAX-->>Cliente: answer
Note over AIVAX,Cliente: Padrao comum em modelos que falam enquanto pensam
AIVAX-->>Cliente: reasoning
AIVAX-->>Cliente: answer
AIVAX->>Ferramentas: Chamadas de ferramenta
Ferramentas-->>AIVAX: Resultados
AIVAX-->>Cliente: tool calls ou servertool
AIVAX-->>Cliente: reasoning
AIVAX-->>Cliente: answer
AIVAX-->>Cliente: stop e usage
AIVAX-->>Cliente: DONE
Por isso, não é uma boa ideia renderizar um painel fixo para raciocínio e outro para conteúdo final como se fossem fluxos independentes. Com AIVAX, uma única inferência também pode resumir vários turnos internos por causa de ferramentas executadas no servidor, como MCP, pesquisa e outras funções embutidas. Se o cliente separar tudo por tipo, o usuário perde a ordem real do que aconteceu.
Streaming SSE
Use stream: true no endpoint POST /v1/chat/completions para receber a resposta em Server-Sent Events (SSE):
{
"model": "my-gateway:50c3",
"messages": [
{
"role": "user",
"content": "Resuma os principais pontos deste documento."
}
],
"stream": true
}
Por padrão, o servidor envia pings periódicos para manter a conexão aberta. Envie o cabeçalho Sse-Stream-Options: no-ping quando o seu cliente ou proxy não aceitar mensagens de keep-alive no SSE.
Cada evento de conteúdo segue o formato chat.completion.chunk:
{
"id": "chatcmpl-...",
"object": "chat.completion.chunk",
"created": 1755874904,
"model": "@openai/gpt-5-mini",
"system_fingerprint": "fp_abc123",
"choices": [
{
"index": 0,
"finish_reason": null,
"logprobs": null,
"delta": {
"role": "assistant",
"content": "A resposta começa aqui"
}
}
],
"usage": null
}
O primeiro chunk útil inclui delta.role: "assistant". Os chunks seguintes podem trazer delta.content, delta.reasoning, delta.tool_calls ou uma combinação desses campos. Chunks sem conteúdo, raciocínio, ferramentas ou uso são ignorados pelo servidor e não chegam ao cliente.
Como renderizar
Dentro de uma resposta em andamento, renderize cada informação na ordem recebida:
delta.content: adiciona texto visível na fala da assistente;delta.reasoning: adiciona um evento de raciocínio no ponto atual da resposta;delta.tool_calls: adiciona ou atualiza uma chamada de ferramenta no ponto atual da resposta;servertool: adiciona ou atualiza um evento de ferramenta executada pelo servidor;finish_reason: "stop": encerra a resposta normalmente;finish_reason: "error": encerra a resposta com erro.
O chat pode estilizar cada tipo de evento de forma diferente, mas a ordem deve continuar sendo uma só. Por exemplo, um trecho de raciocínio pode aparecer como uma linha discreta ou recolhível dentro da própria resposta da assistente, seguido pela ferramenta chamada e depois pelo texto que veio depois. O ponto essencial é não mover todos os raciocínios para uma área separada e todos os textos para outra área, porque isso desmonta a sequência da inferência.
Raciocínio
Quando o modelo ou gateway retorna tokens de raciocínio, o chunk inclui delta.reasoning:
{
"choices": [
{
"index": 0,
"finish_reason": null,
"logprobs": null,
"delta": {
"reasoning": "Analisando os critérios relevantes..."
}
}
]
}
Esse campo não substitui delta.content. Ele representa um evento próprio no fluxo da resposta. Se o cliente optar por mostrar raciocínio, mostre-o na posição em que ele chegou. Se o cliente optar por ocultar raciocínio, preserve a ordem dos demais eventos e continue renderizando conteúdo e ferramentas normalmente.
Ferramentas
Chamadas de ferramenta do modelo chegam em delta.tool_calls usando o formato de function calling:
{
"choices": [
{
"index": 0,
"finish_reason": null,
"delta": {
"tool_calls": [
{
"index": 0,
"id": "call_...",
"type": "function",
"function": {
"name": "search_documents",
"arguments": "{\"query\":\"contrato\"}"
}
}
]
}
}
]
}
O campo function.arguments é entregue como texto e pode representar JSON parcial enquanto a geração ainda está em andamento. Em chats contínuos, atualize a chamada de ferramenta conforme novos dados chegarem e só interprete os argumentos quando estiverem completos.
Ferramentas internas do gateway podem enviar eventos de atualização no mesmo stream. Esses eventos usam choices: [] e o objeto servertool:
{
"id": "chatcmpl-...",
"object": "chat.completion.chunk",
"created": 1755874904,
"model": "@openai/gpt-5-mini",
"system_fingerprint": "fp_abc123",
"choices": [],
"servertool": {
"name": "WebSearch",
"id": "tool_...",
"contents": "{\"query\":\"notícias recentes\"}",
"state": "Running"
},
"usage": null
}
Use servertool para mostrar estados como “pesquisando”, “abrindo link” ou “executando ferramenta”. Esse evento também faz parte da linha do tempo da resposta, mas não deve ser concatenado como texto da assistente.
Uso, finalização e erros
O uso de tokens não acompanha cada chunk. Quando disponível, ele aparece no último chunk antes de [DONE], junto com finish_reason: "stop":
{
"choices": [
{
"index": 0,
"finish_reason": "stop",
"logprobs": null,
"delta": {}
}
],
"usage": {
"prompt_tokens": 84,
"completion_tokens": 16,
"total_tokens": 1892,
"prompt_tokens_details": {
"cached_tokens": 1792,
"audio_tokens": 0
}
}
}
Depois do último chunk, o servidor envia a linha [DONE] e fecha o SSE. Se ocorrer um erro durante o streaming, o servidor envia um chunk com finish_reason: "error", um objeto error com a mensagem segura para o cliente e, em seguida, [DONE].
{
"choices": [
{
"index": 0,
"finish_reason": "error",
"delta": {
"content": ""
}
}
],
"error": {
"code": "server_error",
"message": "Mensagem do erro"
}
}
Respostas JSON em streaming
Quando json_only: true é usado com stream: true, o comportamento muda: o servidor envia o JSON completo gerado pelo modelo como um único evento, depois envia [DONE]. Nesse modo, não há envelope chat.completion.chunk, delta, usage ou metadados de geração no conteúdo enviado.
Português
English