empty string fix
This commit is contained in:
@@ -66,28 +66,27 @@ 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 = '';
|
||||||
|
|
||||||
|
// 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) {
|
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;
|
||||||
|
|
||||||
const delta = json.choices?.[0]?.delta?.content ?? '';
|
|
||||||
|
|
||||||
if (json.choices?.[0]?.finish_reason === 'stop') {
|
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
|
||||||
@@ -95,19 +94,21 @@ async function* completeStream(prompt, options = {}) {
|
|||||||
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());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user