empty string fix

This commit is contained in:
Storme-bit
2026-08-16 06:28:16 -07:00
parent 0248fcb58c
commit b87b2fc930
@@ -66,49 +66,50 @@ async function* completeStream(prompt, options = {}) {
if (!res.ok) if (!res.ok)
throw new Error(`llama.cpp error: ${res.status} ${res.statusText}`); throw new Error(`llama.cpp error: ${res.status} ${res.statusText}`);
//SSE lines can be split across network chunks, so we need to buffer them until we have a complete line // SSE lines can be split across network chunks, so we buffer the incomplete
// tail and only process whole lines.
let buffer = ''; let buffer = '';
function* processLine(line){ // Handles ONE line. Yields at most one content chunk. Never touches `lines`
// or the network buffer — the reader loop below owns that.
function* processLine(line) {
if (!line.startsWith('data: ') || line === 'data: [DONE]') return; if (!line.startsWith('data: ') || line === 'data: [DONE]') return;
let json; let json;
try { try {
json = JSON.parse(line.slice(6)); json = JSON.parse(line.slice(6));
} catch (err) { } catch (err) {
logger.error('[llamacpp] Skipping unparseable SSE line:', line.slice(0,120)); logger.warn('[llamacpp] Skipping unparseable SSE line:', line.slice(0, 120));
return; return;
} }
const delta = json.choices?.[0]?.delta?.content;
if (json.choices?.[0]?.finish_reason === 'stop') {
const delta = json.choices?.[0]?.delta?.content ?? '';
if (json.choices?.[0]?.finish_reason === 'stop' ) {
finalModel = json.model ?? finalModel; finalModel = json.model ?? finalModel;
} }
// usage arrives in a separate final chunk with empty choices array // usage arrives in a separate final chunk with empty choices array
if (json.usage){ if (json.usage) {
finalTokenCount = (json.usage.completion_tokens ?? 0) + (json.usage.prompt_tokens ?? 0); finalTokenCount = (json.usage.completion_tokens ?? 0) + (json.usage.prompt_tokens ?? 0);
} }
for (const line of lines) { if (delta) yield { response: delta, done: false };
yield* processLine(line.trim());
}
} }
for await (const chunk of res.body) { for await (const chunk of res.body) {
buffer += Buffer.from(chunk).toString('utf-8'); buffer += Buffer.from(chunk).toString('utf-8');
const lines = buffer.split('\n'); const lines = buffer.split('\n');
buffer = lines.pop() ?? ""; // keep the last line in the buffer, it may be incomplete buffer = lines.pop() ?? ''; // last element may be incomplete — keep for next chunk
for (const line of lines) {
yield* processLine(line.trim());
}
} }
//Flush anything left in buffer after stream closes // Flush anything left in the buffer after the stream closes
if (buffer.trim()){ if (buffer.trim()) {
yield* processLine(buffer.trim()); yield* processLine(buffer.trim());
} }