Created
June 1, 2025 23:54
-
-
Save celsowm/082aa881413eec853757f691cd8fb548 to your computer and use it in GitHub Desktop.
stream_mode_json_chat_completion.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(async () => { | |
// 1) Ajuste para o URL do seu servidor | |
const ENDPOINT = "http://localhost:8081/v1/chat/completions"; | |
// 2) Use exatamente o mesmo nome de modelo que funcionou no Python | |
const MODEL = "seu-modelo-aqui"; | |
// 3) Prompt de teste (igual ao Python: "Who won the world series in 2020") | |
const PROMPT = "Who won the world series in 2020"; | |
// 4) Definimos o JSON‐schema idêntico ao do Python | |
const schema = { | |
type: "object", | |
properties: { | |
team_name: { type: "string" }, | |
year: { type: "integer" }, | |
world_series_winner: { type: "string" }, | |
opponent: { type: "string" }, | |
series_result: { type: "string" } | |
}, | |
required: ["team_name", "year", "world_series_winner", "opponent", "series_result"], | |
additionalProperties: false | |
}; | |
// 5) Construímos o “system prompt” exatamente como no Python | |
const systemPrompt = | |
"Você responde em formato JSON seguindo estritamente este schema:\n\n" + | |
JSON.stringify(schema, null, 2) + | |
"\n\nNada fora desse objeto JSON."; | |
// 6) Montamos o corpo da requisição, incluindo stream:true e response_format | |
const body = { | |
model: MODEL, | |
messages: [ | |
{ role: "system", content: systemPrompt }, | |
{ role: "user", content: PROMPT } | |
], | |
stream: true, | |
response_format: { | |
type: "json_object", | |
schema | |
}, | |
temperature: 0.7 | |
}; | |
console.log("→ Enviando requisição para o servidor…"); | |
console.log("Request body:", JSON.stringify(body, null, 2)); | |
try { | |
// 7) Fazer o fetch com método POST | |
const resp = await fetch(ENDPOINT, { | |
method: "POST", | |
headers: { "Content-Type": "application/json" }, | |
body: JSON.stringify(body) | |
}); | |
console.log("→ Resposta HTTP recebida. Status:", resp.status); | |
if (!resp.ok) { | |
console.error("Erro HTTP:", resp.status, resp.statusText); | |
return; | |
} | |
// 8) Lêmos o body como ReadableStream | |
const reader = resp.body.getReader(); | |
const decoder = new TextDecoder("utf-8"); | |
let acumulado = ""; // vai armazenar o JSON inteiro | |
console.log("→ Iniciando leitura do stream. Aguarde…"); | |
while (true) { | |
const { done, value } = await reader.read(); | |
if (done) { | |
console.log("→ Stream finalizado."); | |
break; | |
} | |
// 9) Decodifica o chunk atual para string (pode conter múltiplas linhas "data:") | |
const chunkTexto = decoder.decode(value, { stream: true }); | |
console.log("[STREAM] Novo chunk recebido:\n", chunkTexto); | |
// 10) Cada payload “válido” começa com "data:" | |
for (const linhaBruta of chunkTexto.split("\n")) { | |
if (!linhaBruta.trimStart().startsWith("data:")) { | |
continue; | |
} | |
// Remove o prefixo “data:” e espaços em branco | |
const payload = linhaBruta.replace(/^data:\s*/, "").trim(); | |
if (payload === "[DONE]") { | |
console.log("[STREAM] Payload '[DONE]' recebido. Encerrando leitura."); | |
break; | |
} | |
// 11) Tentamos fazer JSON.parse no payload (que deve ter a forma | |
// {"choices":[{"delta":{"content":"...parte do JSON..."}}], ...}) | |
let delta; | |
try { | |
const obj = JSON.parse(payload); | |
delta = obj.choices?.[0]?.delta; | |
} catch (err) { | |
console.warn("[STREAM] Erro ao parsear JSON do payload:", err); | |
continue; | |
} | |
if (!delta || typeof delta.content !== "string") { | |
console.warn("[STREAM] Delta sem conteúdo textual válido. Pulando..."); | |
continue; | |
} | |
// 12) “delta.content” contém um fragmento da string JSON. Exemplo: | |
// "{\"team_name\":\"Los Angeles Dodgers\",\"year\":2020" | |
// (pode ser apenas parte de uma chave/valor, pois é streaming) | |
const fragmento = delta.content.replace(/\r/g, ""); | |
acumulado += fragmento; | |
// 13) Em vez de usar process.stdout.write (Node.js), só logamos: | |
console.log("[FRAGMENTO RECEBIDO]", fragmento); | |
} | |
} | |
// 14) Após sair do while, “acumulado” deve conter o JSON completo (em teoria) | |
const resultadoBruto = acumulado.trim(); | |
console.log("→ Conteúdo bruto acumulado:\n", resultadoBruto); | |
// 15) Tentar parsear o JSON inteiro | |
console.log("→ Tentando dar JSON.parse no resultado completo:"); | |
try { | |
const parsed = JSON.parse(resultadoBruto); | |
console.log(parsed); | |
} catch (err) { | |
console.error("❌ Falha ao parsear JSON final:", err); | |
console.log("Texto bruto para debug:", JSON.stringify(resultadoBruto)); | |
} | |
} catch (err) { | |
console.error("❌ Erro ao fazer fetch/stream:", err); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment