@saltcorn/large-language-model 0.8.10 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/generate.js +107 -7
- package/package.json +1 -1
package/generate.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const node_fetch = require("node-fetch");
|
|
2
2
|
const util = require("util");
|
|
3
3
|
const exec = util.promisify(require("child_process").exec);
|
|
4
4
|
const db = require("@saltcorn/data/db");
|
|
@@ -207,7 +207,10 @@ const getCompletionOpenAICompatible = async (
|
|
|
207
207
|
)
|
|
208
208
|
body.temperature = 0.7;
|
|
209
209
|
}
|
|
210
|
-
|
|
210
|
+
if (rest.streamCallback) {
|
|
211
|
+
body.stream = true;
|
|
212
|
+
delete body.streamCallback;
|
|
213
|
+
}
|
|
211
214
|
if (responses_api) {
|
|
212
215
|
for (const tool of body.tools || []) {
|
|
213
216
|
if (tool.type !== "function") continue;
|
|
@@ -303,18 +306,115 @@ const getCompletionOpenAICompatible = async (
|
|
|
303
306
|
)} to ${chatCompleteEndpoint} headers ${JSON.stringify(headers)}`
|
|
304
307
|
);
|
|
305
308
|
if (debugCollector) debugCollector.request = body;
|
|
306
|
-
|
|
307
|
-
const rawResponse = await fetch(chatCompleteEndpoint, {
|
|
309
|
+
const reqTimeStart = Date.now();
|
|
310
|
+
const rawResponse = await (global.fetch || node_fetch)(chatCompleteEndpoint, {
|
|
308
311
|
method: "POST",
|
|
309
312
|
headers,
|
|
310
313
|
body: JSON.stringify(body),
|
|
311
314
|
});
|
|
315
|
+
let streamParts = [];
|
|
316
|
+
let streamToolCalls = null;
|
|
317
|
+
|
|
318
|
+
if (rest.streamCallback) {
|
|
319
|
+
// https://stackoverflow.com/a/75751803/19839414
|
|
320
|
+
// https://stackoverflow.com/a/57664622/19839414
|
|
321
|
+
await new Promise((resolve, reject) => {
|
|
322
|
+
let dataDone = false;
|
|
323
|
+
let stashed = "";
|
|
324
|
+
|
|
325
|
+
const process_stream_data = (value) => {
|
|
326
|
+
const arr = value.split("\n");
|
|
327
|
+
arr.forEach((data) => {
|
|
328
|
+
if (data.length === 0) return; // ignore empty message
|
|
329
|
+
if (data.startsWith(":")) return; // ignore sse comment message
|
|
330
|
+
if (data === "data: [DONE]") {
|
|
331
|
+
dataDone = true;
|
|
332
|
+
resolve();
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
try {
|
|
336
|
+
const json = JSON.parse(stashed + data.substring(6));
|
|
337
|
+
stashed = "";
|
|
338
|
+
console.log(json.choices[0]);
|
|
339
|
+
|
|
340
|
+
// callback
|
|
341
|
+
|
|
342
|
+
//answer store
|
|
343
|
+
if (json.choices?.[0]?.content)
|
|
344
|
+
streamParts.push(json.choices[0].content);
|
|
345
|
+
if (json.choices?.[0]?.delta?.content)
|
|
346
|
+
streamParts.push(json.choices[0].delta.content);
|
|
347
|
+
if (json.choices?.[0]?.delta?.tool_calls) {
|
|
348
|
+
if (!streamToolCalls) streamToolCalls = json.choices?.[0]?.delta;
|
|
349
|
+
else
|
|
350
|
+
json.choices?.[0]?.delta?.tool_calls.forEach((tc, ix) => {
|
|
351
|
+
streamToolCalls.tool_calls[ix].function.arguments +=
|
|
352
|
+
tc.function.arguments;
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
rest.streamCallback(json);
|
|
356
|
+
} catch (e) {
|
|
357
|
+
//console.error(e);
|
|
358
|
+
stashed = data.substring(6);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
};
|
|
362
|
+
if (global.fetch) {
|
|
363
|
+
const reader = rawResponse.body
|
|
364
|
+
?.pipeThrough(new TextDecoderStream())
|
|
365
|
+
.getReader();
|
|
366
|
+
if (!reader) return;
|
|
367
|
+
// eslint-disable-next-line no-constant-condition
|
|
368
|
+
(async () => {
|
|
369
|
+
while (!dataDone) {
|
|
370
|
+
// eslint-disable-next-line no-await-in-loop
|
|
371
|
+
|
|
372
|
+
const { value, done } = await reader.read();
|
|
373
|
+
|
|
374
|
+
if (done) {
|
|
375
|
+
dataDone = true;
|
|
376
|
+
resolve();
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
process_stream_data(value);
|
|
380
|
+
if (dataDone) break;
|
|
381
|
+
}
|
|
382
|
+
})().catch((e) => {
|
|
383
|
+
//console.error(e);
|
|
384
|
+
dataDone = true;
|
|
385
|
+
reject(e);
|
|
386
|
+
});
|
|
387
|
+
} else
|
|
388
|
+
rawResponse.body.on("readable", () => {
|
|
389
|
+
let chunk;
|
|
390
|
+
while (null !== (chunk = rawResponse.body.read())) {
|
|
391
|
+
let value = chunk.toString();
|
|
392
|
+
process_stream_data(value);
|
|
393
|
+
if (dataDone) break;
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
if (debugCollector) {
|
|
398
|
+
//TODO get the full response
|
|
399
|
+
if (streamToolCalls) debugCollector.response = streamToolCalls;
|
|
400
|
+
debugCollector.response_time_ms = Date.now() - reqTimeStart;
|
|
401
|
+
}
|
|
402
|
+
return streamToolCalls
|
|
403
|
+
? {
|
|
404
|
+
content: streamParts.join(""),
|
|
405
|
+
tool_calls: streamToolCalls.tool_calls,
|
|
406
|
+
}
|
|
407
|
+
: streamParts.join("");
|
|
408
|
+
}
|
|
312
409
|
const results = await rawResponse.json();
|
|
313
410
|
//console.log("results", results);
|
|
314
411
|
if (debugResult)
|
|
315
412
|
console.log("OpenAI response", JSON.stringify(results, null, 2));
|
|
316
413
|
else getState().log(6, `OpenAI response ${JSON.stringify(results)}`);
|
|
317
|
-
if (debugCollector)
|
|
414
|
+
if (debugCollector) {
|
|
415
|
+
debugCollector.response = results;
|
|
416
|
+
debugCollector.response_time_ms = Date.now() - reqTimeStart;
|
|
417
|
+
}
|
|
318
418
|
|
|
319
419
|
if (results.error) throw new Error(`OpenAI error: ${results.error.message}`);
|
|
320
420
|
if (responses_api) {
|
|
@@ -395,7 +495,7 @@ const getImageGenOpenAICompatible = async (
|
|
|
395
495
|
if (debugResult) console.log("OpenAI image request", imageEndpoint, body);
|
|
396
496
|
if (debugCollector) debugCollector.request = body;
|
|
397
497
|
|
|
398
|
-
const rawResponse = await fetch(imageEndpoint, {
|
|
498
|
+
const rawResponse = await (global.fetch || node_fetch)(imageEndpoint, {
|
|
399
499
|
method: "POST",
|
|
400
500
|
headers,
|
|
401
501
|
body: JSON.stringify(body),
|
|
@@ -424,7 +524,7 @@ const getEmbeddingOpenAICompatible = async (
|
|
|
424
524
|
input: prompt,
|
|
425
525
|
};
|
|
426
526
|
|
|
427
|
-
const rawResponse = await fetch(embeddingsEndpoint, {
|
|
527
|
+
const rawResponse = await (global.fetch || node_fetch)(embeddingsEndpoint, {
|
|
428
528
|
method: "POST",
|
|
429
529
|
headers,
|
|
430
530
|
body: JSON.stringify(body),
|