Cada evento é data: seguido do JSON, uma linha em branco separando frames.
Respostas de chat chegam em partes, por Server-Sent Events.
O erro mais comum é bufferizar a resposta inteira antes de repassar (por exemplo, num proxy que faz await response.text()). Isso anula o streaming e a UI trava até o fim gerar.
SSE[DONE]AbortController
O último frame é sempre data: [DONE] — pare de ler ao vê-lo.
Use AbortController para interromper a leitura a qualquer momento, sem esperar o servidor terminar.
Consumindo em JavaScript
response.body é um ReadableStream — leia direto, sem bibliotecas extras.
fetch + streaming
1const response = await fetch("https://api.3mm.com.br/v1/chat/completions", {2 method: "POST",3 headers: {4 Authorization: `Bearer ${process.env.THREEM_API_KEY}`,5 "Content-Type": "application/json",6 },7 body: JSON.stringify({8 model: "3m-chat-pro",9 stream: true,10 messages: [{ role: "user", content: "Conte até 5." }],11 }),12});13 14const reader = response.body.getReader();15const decoder = new TextDecoder();16 17for (;;) {18 const { done, value } = await reader.read();19 if (done) break;20 process.stdout.write(decoder.decode(value));21}Consumindo em Python
httpx.stream lê linha por linha sem carregar a resposta inteira na memória.
httpx.stream
1import httpx, os2 3with httpx.stream(4 "POST",5 "https://api.3mm.com.br/v1/chat/completions",6 headers={"Authorization": f"Bearer {os.environ['THREEM_API_KEY']}"},7 json={"model": "3m-chat-pro", "stream": True,8 "messages": [{"role": "user", "content": "Conte até 5."}]},9) as response:10 for line in response.iter_lines():11 if line.startswith("data:"):12 print(line[5:].strip())Streaming também pode falhar no meio — veja os status e formatos de erro na próxima página.
Ver erros