@elizaos/plugin-local-ai 1.0.0 → 1.0.2
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/dist/index.js +254 -374
- package/dist/index.js.map +1 -1
- package/package.json +45 -4
package/dist/index.js
CHANGED
|
@@ -3,10 +3,7 @@ import fs5 from "fs";
|
|
|
3
3
|
import os3 from "os";
|
|
4
4
|
import path5 from "path";
|
|
5
5
|
import { Readable as Readable2 } from "stream";
|
|
6
|
-
import {
|
|
7
|
-
ModelType,
|
|
8
|
-
logger as logger8
|
|
9
|
-
} from "@elizaos/core";
|
|
6
|
+
import { ModelType, logger as logger8 } from "@elizaos/core";
|
|
10
7
|
import {
|
|
11
8
|
LlamaChatSession,
|
|
12
9
|
getLlama
|
|
@@ -253,10 +250,7 @@ var DownloadManager = class _DownloadManager {
|
|
|
253
250
|
reject(new Error(`Failed to download: ${response.statusCode}`));
|
|
254
251
|
return;
|
|
255
252
|
}
|
|
256
|
-
const totalSize = Number.parseInt(
|
|
257
|
-
response.headers["content-length"] || "0",
|
|
258
|
-
10
|
|
259
|
-
);
|
|
253
|
+
const totalSize = Number.parseInt(response.headers["content-length"] || "0", 10);
|
|
260
254
|
let downloadedSize = 0;
|
|
261
255
|
let lastLoggedPercent = 0;
|
|
262
256
|
const barLength = 30;
|
|
@@ -267,13 +261,9 @@ var DownloadManager = class _DownloadManager {
|
|
|
267
261
|
downloadedSize += chunk.length;
|
|
268
262
|
const percent = Math.round(downloadedSize / totalSize * 100);
|
|
269
263
|
if (percent >= lastLoggedPercent + 5) {
|
|
270
|
-
const filledLength = Math.floor(
|
|
271
|
-
downloadedSize / totalSize * barLength
|
|
272
|
-
);
|
|
264
|
+
const filledLength = Math.floor(downloadedSize / totalSize * barLength);
|
|
273
265
|
const progressBar = "\u25B0".repeat(filledLength) + "\u25B1".repeat(barLength - filledLength);
|
|
274
|
-
logger2.info(
|
|
275
|
-
`Downloading ${fileName}: ${progressBar} ${percent}%`
|
|
276
|
-
);
|
|
266
|
+
logger2.info(`Downloading ${fileName}: ${progressBar} ${percent}%`);
|
|
277
267
|
lastLoggedPercent = percent;
|
|
278
268
|
}
|
|
279
269
|
});
|
|
@@ -288,24 +278,18 @@ var DownloadManager = class _DownloadManager {
|
|
|
288
278
|
fs.mkdirSync(destDir, { recursive: true });
|
|
289
279
|
}
|
|
290
280
|
if (!fs.existsSync(tempPath)) {
|
|
291
|
-
reject(
|
|
292
|
-
new Error(`Temporary file ${tempPath} does not exist`)
|
|
293
|
-
);
|
|
281
|
+
reject(new Error(`Temporary file ${tempPath} does not exist`));
|
|
294
282
|
return;
|
|
295
283
|
}
|
|
296
284
|
if (fs.existsSync(destPath)) {
|
|
297
285
|
try {
|
|
298
286
|
const backupPath = `${destPath}.bak`;
|
|
299
287
|
fs.renameSync(destPath, backupPath);
|
|
300
|
-
logger2.info(
|
|
301
|
-
`Created backup of existing file: ${backupPath}`
|
|
302
|
-
);
|
|
288
|
+
logger2.info(`Created backup of existing file: ${backupPath}`);
|
|
303
289
|
fs.renameSync(tempPath, destPath);
|
|
304
290
|
if (fs.existsSync(backupPath)) {
|
|
305
291
|
fs.unlinkSync(backupPath);
|
|
306
|
-
logger2.info(
|
|
307
|
-
`Removed backup file after successful update: ${backupPath}`
|
|
308
|
-
);
|
|
292
|
+
logger2.info(`Removed backup file after successful update: ${backupPath}`);
|
|
309
293
|
}
|
|
310
294
|
} catch (moveErr) {
|
|
311
295
|
logger2.error(
|
|
@@ -315,9 +299,7 @@ var DownloadManager = class _DownloadManager {
|
|
|
315
299
|
if (fs.existsSync(backupPath)) {
|
|
316
300
|
try {
|
|
317
301
|
fs.renameSync(backupPath, destPath);
|
|
318
|
-
logger2.info(
|
|
319
|
-
`Restored from backup after failed update: ${backupPath}`
|
|
320
|
-
);
|
|
302
|
+
logger2.info(`Restored from backup after failed update: ${backupPath}`);
|
|
321
303
|
} catch (restoreErr) {
|
|
322
304
|
logger2.error(
|
|
323
305
|
`Failed to restore from backup: ${restoreErr instanceof Error ? restoreErr.message : String(restoreErr)}`
|
|
@@ -339,9 +321,7 @@ var DownloadManager = class _DownloadManager {
|
|
|
339
321
|
} else {
|
|
340
322
|
fs.renameSync(tempPath, destPath);
|
|
341
323
|
}
|
|
342
|
-
logger2.success(
|
|
343
|
-
`Download of ${fileName} completed successfully`
|
|
344
|
-
);
|
|
324
|
+
logger2.success(`Download of ${fileName} completed successfully`);
|
|
345
325
|
this.activeDownloads.delete(destPath);
|
|
346
326
|
resolve();
|
|
347
327
|
} catch (err) {
|
|
@@ -363,9 +343,7 @@ var DownloadManager = class _DownloadManager {
|
|
|
363
343
|
});
|
|
364
344
|
});
|
|
365
345
|
file.on("error", (err) => {
|
|
366
|
-
logger2.error(
|
|
367
|
-
`File write error: ${err instanceof Error ? err.message : String(err)}`
|
|
368
|
-
);
|
|
346
|
+
logger2.error(`File write error: ${err instanceof Error ? err.message : String(err)}`);
|
|
369
347
|
file.close(() => {
|
|
370
348
|
if (fs.existsSync(tempPath)) {
|
|
371
349
|
try {
|
|
@@ -383,9 +361,7 @@ var DownloadManager = class _DownloadManager {
|
|
|
383
361
|
}
|
|
384
362
|
);
|
|
385
363
|
request.on("error", (err) => {
|
|
386
|
-
logger2.error(
|
|
387
|
-
`Request error: ${err instanceof Error ? err.message : String(err)}`
|
|
388
|
-
);
|
|
364
|
+
logger2.error(`Request error: ${err instanceof Error ? err.message : String(err)}`);
|
|
389
365
|
if (fs.existsSync(tempPath)) {
|
|
390
366
|
try {
|
|
391
367
|
fs.unlinkSync(tempPath);
|
|
@@ -424,9 +400,7 @@ var DownloadManager = class _DownloadManager {
|
|
|
424
400
|
*/
|
|
425
401
|
async downloadFile(url, destPath) {
|
|
426
402
|
if (this.activeDownloads.has(destPath)) {
|
|
427
|
-
logger2.info(
|
|
428
|
-
`Download for ${destPath} already in progress, waiting for it to complete...`
|
|
429
|
-
);
|
|
403
|
+
logger2.info(`Download for ${destPath} already in progress, waiting for it to complete...`);
|
|
430
404
|
const existingDownload = this.activeDownloads.get(destPath);
|
|
431
405
|
if (existingDownload) {
|
|
432
406
|
return existingDownload;
|
|
@@ -665,9 +639,7 @@ var PlatformManager = class _PlatformManager {
|
|
|
665
639
|
isAppleSilicon: true
|
|
666
640
|
};
|
|
667
641
|
}
|
|
668
|
-
const { stdout: gpuInfo } = await execAsync(
|
|
669
|
-
"system_profiler SPDisplaysDataType"
|
|
670
|
-
);
|
|
642
|
+
const { stdout: gpuInfo } = await execAsync("system_profiler SPDisplaysDataType");
|
|
671
643
|
return {
|
|
672
644
|
name: gpuInfo.split("Chipset Model:")[1]?.split("\n")[0]?.trim() || "Unknown GPU",
|
|
673
645
|
type: "metal",
|
|
@@ -689,9 +661,7 @@ var PlatformManager = class _PlatformManager {
|
|
|
689
661
|
*/
|
|
690
662
|
async detectWindowsGPU() {
|
|
691
663
|
try {
|
|
692
|
-
const { stdout } = await execAsync(
|
|
693
|
-
"wmic path win32_VideoController get name"
|
|
694
|
-
);
|
|
664
|
+
const { stdout } = await execAsync("wmic path win32_VideoController get name");
|
|
695
665
|
const gpuName = stdout.split("\n")[1].trim();
|
|
696
666
|
if (gpuName.toLowerCase().includes("nvidia")) {
|
|
697
667
|
const { stdout: nvidiaInfo } = await execAsync(
|
|
@@ -885,9 +855,7 @@ var getPlatformManager = () => {
|
|
|
885
855
|
|
|
886
856
|
// src/utils/tokenizerManager.ts
|
|
887
857
|
import { logger as logger4 } from "@elizaos/core";
|
|
888
|
-
import {
|
|
889
|
-
AutoTokenizer
|
|
890
|
-
} from "@huggingface/transformers";
|
|
858
|
+
import { AutoTokenizer } from "@huggingface/transformers";
|
|
891
859
|
var TokenizerManager = class _TokenizerManager {
|
|
892
860
|
static instance = null;
|
|
893
861
|
tokenizers;
|
|
@@ -937,18 +905,13 @@ var TokenizerManager = class _TokenizerManager {
|
|
|
937
905
|
logger4.info("Using cached tokenizer:", { key: tokenizerKey });
|
|
938
906
|
const cachedTokenizer = this.tokenizers.get(tokenizerKey);
|
|
939
907
|
if (!cachedTokenizer) {
|
|
940
|
-
throw new Error(
|
|
941
|
-
`Tokenizer ${tokenizerKey} exists in map but returned undefined`
|
|
942
|
-
);
|
|
908
|
+
throw new Error(`Tokenizer ${tokenizerKey} exists in map but returned undefined`);
|
|
943
909
|
}
|
|
944
910
|
return cachedTokenizer;
|
|
945
911
|
}
|
|
946
912
|
const fs6 = await import("fs");
|
|
947
913
|
if (!fs6.existsSync(this.modelsDir)) {
|
|
948
|
-
logger4.warn(
|
|
949
|
-
"Models directory does not exist, creating it:",
|
|
950
|
-
this.modelsDir
|
|
951
|
-
);
|
|
914
|
+
logger4.warn("Models directory does not exist, creating it:", this.modelsDir);
|
|
952
915
|
fs6.mkdirSync(this.modelsDir, { recursive: true });
|
|
953
916
|
}
|
|
954
917
|
logger4.info(
|
|
@@ -956,13 +919,10 @@ var TokenizerManager = class _TokenizerManager {
|
|
|
956
919
|
this.modelsDir
|
|
957
920
|
);
|
|
958
921
|
try {
|
|
959
|
-
const tokenizer = await AutoTokenizer.from_pretrained(
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
local_files_only: false
|
|
964
|
-
}
|
|
965
|
-
);
|
|
922
|
+
const tokenizer = await AutoTokenizer.from_pretrained(modelConfig.tokenizer.name, {
|
|
923
|
+
cache_dir: this.modelsDir,
|
|
924
|
+
local_files_only: false
|
|
925
|
+
});
|
|
966
926
|
this.tokenizers.set(tokenizerKey, tokenizer);
|
|
967
927
|
logger4.success("Tokenizer loaded successfully:", { key: tokenizerKey });
|
|
968
928
|
return tokenizer;
|
|
@@ -974,13 +934,10 @@ var TokenizerManager = class _TokenizerManager {
|
|
|
974
934
|
modelsDir: this.modelsDir
|
|
975
935
|
});
|
|
976
936
|
logger4.info("Retrying tokenizer loading...");
|
|
977
|
-
const tokenizer = await AutoTokenizer.from_pretrained(
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
local_files_only: false
|
|
982
|
-
}
|
|
983
|
-
);
|
|
937
|
+
const tokenizer = await AutoTokenizer.from_pretrained(modelConfig.tokenizer.name, {
|
|
938
|
+
cache_dir: this.modelsDir,
|
|
939
|
+
local_files_only: false
|
|
940
|
+
});
|
|
984
941
|
this.tokenizers.set(tokenizerKey, tokenizer);
|
|
985
942
|
logger4.success("Tokenizer loaded successfully on retry:", {
|
|
986
943
|
key: tokenizerKey
|
|
@@ -1078,8 +1035,15 @@ import fs2 from "fs";
|
|
|
1078
1035
|
import path2 from "path";
|
|
1079
1036
|
import { promisify as promisify2 } from "util";
|
|
1080
1037
|
import { logger as logger5 } from "@elizaos/core";
|
|
1081
|
-
import { nodewhisper } from "nodejs-whisper";
|
|
1082
1038
|
var execAsync2 = promisify2(exec2);
|
|
1039
|
+
var whisperModule = null;
|
|
1040
|
+
async function getWhisper() {
|
|
1041
|
+
if (!whisperModule) {
|
|
1042
|
+
const module = await import("whisper-node");
|
|
1043
|
+
whisperModule = module.whisper;
|
|
1044
|
+
}
|
|
1045
|
+
return whisperModule;
|
|
1046
|
+
}
|
|
1083
1047
|
var TranscribeManager = class _TranscribeManager {
|
|
1084
1048
|
static instance = null;
|
|
1085
1049
|
cacheDir;
|
|
@@ -1204,9 +1168,7 @@ var TranscribeManager = class _TranscribeManager {
|
|
|
1204
1168
|
*/
|
|
1205
1169
|
async checkFFmpegAvailability() {
|
|
1206
1170
|
try {
|
|
1207
|
-
const { stdout, stderr } = await execAsync2(
|
|
1208
|
-
"which ffmpeg || where ffmpeg"
|
|
1209
|
-
);
|
|
1171
|
+
const { stdout, stderr } = await execAsync2("which ffmpeg || where ffmpeg");
|
|
1210
1172
|
this.ffmpegPath = stdout.trim();
|
|
1211
1173
|
this.ffmpegAvailable = true;
|
|
1212
1174
|
logger5.info("FFmpeg found at:", {
|
|
@@ -1234,9 +1196,7 @@ var TranscribeManager = class _TranscribeManager {
|
|
|
1234
1196
|
const { stdout } = await execAsync2("ffmpeg -codecs");
|
|
1235
1197
|
const hasRequiredCodecs = stdout.includes("pcm_s16le") && stdout.includes("wav");
|
|
1236
1198
|
if (!hasRequiredCodecs) {
|
|
1237
|
-
throw new Error(
|
|
1238
|
-
"FFmpeg installation missing required codecs (pcm_s16le, wav)"
|
|
1239
|
-
);
|
|
1199
|
+
throw new Error("FFmpeg installation missing required codecs (pcm_s16le, wav)");
|
|
1240
1200
|
}
|
|
1241
1201
|
} catch (error) {
|
|
1242
1202
|
logger5.error("FFmpeg capabilities verification failed:", {
|
|
@@ -1250,20 +1210,17 @@ var TranscribeManager = class _TranscribeManager {
|
|
|
1250
1210
|
* Logs instructions on how to install FFmpeg if it is not properly installed.
|
|
1251
1211
|
*/
|
|
1252
1212
|
logFFmpegInstallInstructions() {
|
|
1253
|
-
logger5.warn(
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1265
|
-
}
|
|
1266
|
-
);
|
|
1213
|
+
logger5.warn("FFmpeg is required but not properly installed. Please install FFmpeg:", {
|
|
1214
|
+
instructions: {
|
|
1215
|
+
mac: "brew install ffmpeg",
|
|
1216
|
+
ubuntu: "sudo apt-get install ffmpeg",
|
|
1217
|
+
windows: "choco install ffmpeg",
|
|
1218
|
+
manual: "Download from https://ffmpeg.org/download.html"
|
|
1219
|
+
},
|
|
1220
|
+
requiredVersion: "4.0 or later",
|
|
1221
|
+
requiredCodecs: ["pcm_s16le", "wav"],
|
|
1222
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1223
|
+
});
|
|
1267
1224
|
}
|
|
1268
1225
|
/**
|
|
1269
1226
|
* Gets the singleton instance of TranscribeManager, creates a new instance if it doesn't exist.
|
|
@@ -1341,17 +1298,29 @@ var TranscribeManager = class _TranscribeManager {
|
|
|
1341
1298
|
*/
|
|
1342
1299
|
async preprocessAudio(audioBuffer) {
|
|
1343
1300
|
if (!this.ffmpegAvailable) {
|
|
1344
|
-
throw new Error(
|
|
1345
|
-
"FFmpeg is not installed. Please install FFmpeg to use audio transcription."
|
|
1346
|
-
);
|
|
1301
|
+
throw new Error("FFmpeg is not installed. Please install FFmpeg to use audio transcription.");
|
|
1347
1302
|
}
|
|
1348
1303
|
try {
|
|
1349
|
-
const
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
);
|
|
1304
|
+
const isWav = audioBuffer.length > 4 && audioBuffer.toString("ascii", 0, 4) === "RIFF" && audioBuffer.length > 12 && audioBuffer.toString("ascii", 8, 12) === "WAVE";
|
|
1305
|
+
const extension = isWav ? ".wav" : "";
|
|
1306
|
+
const tempInputFile = path2.join(this.cacheDir, `temp_input_${Date.now()}${extension}`);
|
|
1353
1307
|
const tempWavFile = path2.join(this.cacheDir, `temp_${Date.now()}.wav`);
|
|
1354
1308
|
fs2.writeFileSync(tempInputFile, audioBuffer);
|
|
1309
|
+
if (isWav) {
|
|
1310
|
+
try {
|
|
1311
|
+
const { stdout } = await execAsync2(
|
|
1312
|
+
`ffprobe -v error -show_entries stream=sample_rate,channels,bits_per_raw_sample -of json "${tempInputFile}"`
|
|
1313
|
+
);
|
|
1314
|
+
const probeResult = JSON.parse(stdout);
|
|
1315
|
+
const stream = probeResult.streams?.[0];
|
|
1316
|
+
if (stream?.sample_rate === "16000" && stream?.channels === 1 && (stream?.bits_per_raw_sample === 16 || stream?.bits_per_raw_sample === void 0)) {
|
|
1317
|
+
fs2.renameSync(tempInputFile, tempWavFile);
|
|
1318
|
+
return tempWavFile;
|
|
1319
|
+
}
|
|
1320
|
+
} catch (probeError) {
|
|
1321
|
+
logger5.debug("FFprobe failed, continuing with conversion:", probeError);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1355
1324
|
await this.convertToWav(tempInputFile, tempWavFile);
|
|
1356
1325
|
if (fs2.existsSync(tempInputFile)) {
|
|
1357
1326
|
fs2.unlinkSync(tempInputFile);
|
|
@@ -1386,36 +1355,46 @@ var TranscribeManager = class _TranscribeManager {
|
|
|
1386
1355
|
try {
|
|
1387
1356
|
const wavFile = await this.preprocessAudio(audioBuffer);
|
|
1388
1357
|
logger5.info("Starting transcription with whisper...");
|
|
1389
|
-
|
|
1390
|
-
const originalStderrWrite = process.stderr.write;
|
|
1391
|
-
const noopWrite = () => true;
|
|
1392
|
-
process.stdout.write = noopWrite;
|
|
1393
|
-
process.stderr.write = noopWrite;
|
|
1394
|
-
let output;
|
|
1358
|
+
let segments;
|
|
1395
1359
|
try {
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1360
|
+
const whisper = await getWhisper();
|
|
1361
|
+
segments = await whisper(wavFile, {
|
|
1362
|
+
modelName: "tiny",
|
|
1363
|
+
modelPath: path2.join(this.cacheDir, "models"),
|
|
1364
|
+
// Specify where to store models
|
|
1400
1365
|
whisperOptions: {
|
|
1401
|
-
|
|
1402
|
-
|
|
1366
|
+
language: "en",
|
|
1367
|
+
word_timestamps: false
|
|
1368
|
+
// We don't need word-level timestamps
|
|
1403
1369
|
}
|
|
1404
1370
|
});
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1371
|
+
} catch (whisperError) {
|
|
1372
|
+
const errorMessage = whisperError instanceof Error ? whisperError.message : String(whisperError);
|
|
1373
|
+
if (errorMessage.includes("not found") || errorMessage.includes("download")) {
|
|
1374
|
+
logger5.error("Whisper model not found. Please run: npx whisper-node download");
|
|
1375
|
+
throw new Error(
|
|
1376
|
+
"Whisper model not found. Please install it with: npx whisper-node download"
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
1379
|
+
logger5.error("Whisper transcription error:", whisperError);
|
|
1380
|
+
throw whisperError;
|
|
1408
1381
|
}
|
|
1409
1382
|
if (fs2.existsSync(wavFile)) {
|
|
1410
1383
|
fs2.unlinkSync(wavFile);
|
|
1411
1384
|
logger5.info("Temporary WAV file cleaned up");
|
|
1412
1385
|
}
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
return
|
|
1416
|
-
}
|
|
1386
|
+
if (!segments || !Array.isArray(segments)) {
|
|
1387
|
+
logger5.warn("Whisper returned no segments (likely silence or very short audio)");
|
|
1388
|
+
return { text: "" };
|
|
1389
|
+
}
|
|
1390
|
+
if (segments.length === 0) {
|
|
1391
|
+
logger5.warn("No speech detected in audio");
|
|
1392
|
+
return { text: "" };
|
|
1393
|
+
}
|
|
1394
|
+
const cleanText = segments.map((segment) => segment.speech?.trim() || "").filter((text) => text).join(" ");
|
|
1417
1395
|
logger5.success("Transcription complete:", {
|
|
1418
1396
|
textLength: cleanText.length,
|
|
1397
|
+
segmentCount: segments.length,
|
|
1419
1398
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1420
1399
|
});
|
|
1421
1400
|
return { text: cleanText };
|
|
@@ -1431,12 +1410,45 @@ var TranscribeManager = class _TranscribeManager {
|
|
|
1431
1410
|
};
|
|
1432
1411
|
|
|
1433
1412
|
// src/utils/ttsManager.ts
|
|
1413
|
+
import { logger as logger6 } from "@elizaos/core";
|
|
1414
|
+
import { pipeline } from "@huggingface/transformers";
|
|
1434
1415
|
import fs3 from "fs";
|
|
1435
1416
|
import path3 from "path";
|
|
1436
|
-
import { Readable } from "stream";
|
|
1437
|
-
import { logger as logger6, prependWavHeader } from "@elizaos/core";
|
|
1438
|
-
import { pipeline } from "@huggingface/transformers";
|
|
1439
1417
|
import { fetch as fetch2 } from "undici";
|
|
1418
|
+
import { PassThrough, Readable } from "stream";
|
|
1419
|
+
function getWavHeader(audioLength, sampleRate, channelCount = 1, bitsPerSample = 16) {
|
|
1420
|
+
const wavHeader = Buffer.alloc(44);
|
|
1421
|
+
wavHeader.write("RIFF", 0);
|
|
1422
|
+
wavHeader.writeUInt32LE(36 + audioLength, 4);
|
|
1423
|
+
wavHeader.write("WAVE", 8);
|
|
1424
|
+
wavHeader.write("fmt ", 12);
|
|
1425
|
+
wavHeader.writeUInt32LE(16, 16);
|
|
1426
|
+
wavHeader.writeUInt16LE(1, 20);
|
|
1427
|
+
wavHeader.writeUInt16LE(channelCount, 22);
|
|
1428
|
+
wavHeader.writeUInt32LE(sampleRate, 24);
|
|
1429
|
+
wavHeader.writeUInt32LE(sampleRate * bitsPerSample * channelCount / 8, 28);
|
|
1430
|
+
wavHeader.writeUInt16LE(bitsPerSample * channelCount / 8, 32);
|
|
1431
|
+
wavHeader.writeUInt16LE(bitsPerSample, 34);
|
|
1432
|
+
wavHeader.write("data", 36);
|
|
1433
|
+
wavHeader.writeUInt32LE(audioLength, 40);
|
|
1434
|
+
return wavHeader;
|
|
1435
|
+
}
|
|
1436
|
+
function prependWavHeader(readable, audioLength, sampleRate, channelCount = 1, bitsPerSample = 16) {
|
|
1437
|
+
const wavHeader = getWavHeader(audioLength, sampleRate, channelCount, bitsPerSample);
|
|
1438
|
+
let pushedHeader = false;
|
|
1439
|
+
const passThrough = new PassThrough();
|
|
1440
|
+
readable.on("data", (data) => {
|
|
1441
|
+
if (!pushedHeader) {
|
|
1442
|
+
passThrough.push(wavHeader);
|
|
1443
|
+
pushedHeader = true;
|
|
1444
|
+
}
|
|
1445
|
+
passThrough.push(data);
|
|
1446
|
+
});
|
|
1447
|
+
readable.on("end", () => {
|
|
1448
|
+
passThrough.end();
|
|
1449
|
+
});
|
|
1450
|
+
return passThrough;
|
|
1451
|
+
}
|
|
1440
1452
|
var TTSManager = class _TTSManager {
|
|
1441
1453
|
static instance = null;
|
|
1442
1454
|
cacheDir;
|
|
@@ -1463,9 +1475,7 @@ var TTSManager = class _TTSManager {
|
|
|
1463
1475
|
}
|
|
1464
1476
|
async initialize() {
|
|
1465
1477
|
if (this.initializingPromise) {
|
|
1466
|
-
logger6.debug(
|
|
1467
|
-
"TTS initialization already in progress, awaiting existing promise."
|
|
1468
|
-
);
|
|
1478
|
+
logger6.debug("TTS initialization already in progress, awaiting existing promise.");
|
|
1469
1479
|
return this.initializingPromise;
|
|
1470
1480
|
}
|
|
1471
1481
|
if (this.initialized) {
|
|
@@ -1477,21 +1487,15 @@ var TTSManager = class _TTSManager {
|
|
|
1477
1487
|
logger6.info("Initializing TTS with Transformers.js backend...");
|
|
1478
1488
|
const ttsModelSpec = MODEL_SPECS.tts.default;
|
|
1479
1489
|
if (!ttsModelSpec) {
|
|
1480
|
-
throw new Error(
|
|
1481
|
-
"Default TTS model specification not found in MODEL_SPECS."
|
|
1482
|
-
);
|
|
1490
|
+
throw new Error("Default TTS model specification not found in MODEL_SPECS.");
|
|
1483
1491
|
}
|
|
1484
1492
|
const modelName = ttsModelSpec.modelId;
|
|
1485
1493
|
const speakerEmbeddingUrl = ttsModelSpec.defaultSpeakerEmbeddingUrl;
|
|
1486
1494
|
logger6.info(`Loading TTS pipeline for model: ${modelName}`);
|
|
1487
1495
|
this.synthesizer = await pipeline("text-to-audio", modelName);
|
|
1488
|
-
logger6.success(
|
|
1489
|
-
`TTS pipeline loaded successfully for model: ${modelName}`
|
|
1490
|
-
);
|
|
1496
|
+
logger6.success(`TTS pipeline loaded successfully for model: ${modelName}`);
|
|
1491
1497
|
if (speakerEmbeddingUrl) {
|
|
1492
|
-
const embeddingFilename = path3.basename(
|
|
1493
|
-
new URL(speakerEmbeddingUrl).pathname
|
|
1494
|
-
);
|
|
1498
|
+
const embeddingFilename = path3.basename(new URL(speakerEmbeddingUrl).pathname);
|
|
1495
1499
|
const embeddingPath = path3.join(this.cacheDir, embeddingFilename);
|
|
1496
1500
|
if (fs3.existsSync(embeddingPath)) {
|
|
1497
1501
|
logger6.info("Loading default speaker embedding from cache...");
|
|
@@ -1503,14 +1507,10 @@ var TTSManager = class _TTSManager {
|
|
|
1503
1507
|
);
|
|
1504
1508
|
logger6.success("Default speaker embedding loaded from cache.");
|
|
1505
1509
|
} else {
|
|
1506
|
-
logger6.info(
|
|
1507
|
-
`Downloading default speaker embedding from: ${speakerEmbeddingUrl}`
|
|
1508
|
-
);
|
|
1510
|
+
logger6.info(`Downloading default speaker embedding from: ${speakerEmbeddingUrl}`);
|
|
1509
1511
|
const response = await fetch2(speakerEmbeddingUrl);
|
|
1510
1512
|
if (!response.ok) {
|
|
1511
|
-
throw new Error(
|
|
1512
|
-
`Failed to download speaker embedding: ${response.statusText}`
|
|
1513
|
-
);
|
|
1513
|
+
throw new Error(`Failed to download speaker embedding: ${response.statusText}`);
|
|
1514
1514
|
}
|
|
1515
1515
|
const buffer = await response.arrayBuffer();
|
|
1516
1516
|
this.defaultSpeakerEmbedding = new Float32Array(buffer);
|
|
@@ -1539,9 +1539,7 @@ var TTSManager = class _TTSManager {
|
|
|
1539
1539
|
throw error;
|
|
1540
1540
|
} finally {
|
|
1541
1541
|
this.initializingPromise = null;
|
|
1542
|
-
logger6.debug(
|
|
1543
|
-
"TTS initializingPromise cleared after completion/failure."
|
|
1544
|
-
);
|
|
1542
|
+
logger6.debug("TTS initializingPromise cleared after completion/failure.");
|
|
1545
1543
|
}
|
|
1546
1544
|
})();
|
|
1547
1545
|
return this.initializingPromise;
|
|
@@ -1650,10 +1648,7 @@ var VisionManager = class _VisionManager {
|
|
|
1650
1648
|
this.modelsDir = path4.join(path4.dirname(cacheDir), "models", "vision");
|
|
1651
1649
|
this.cacheDir = cacheDir;
|
|
1652
1650
|
this.ensureModelsDirExists();
|
|
1653
|
-
this.downloadManager = DownloadManager.getInstance(
|
|
1654
|
-
this.cacheDir,
|
|
1655
|
-
this.modelsDir
|
|
1656
|
-
);
|
|
1651
|
+
this.downloadManager = DownloadManager.getInstance(this.cacheDir, this.modelsDir);
|
|
1657
1652
|
this.platformConfig = this.getPlatformConfig();
|
|
1658
1653
|
logger7.debug("VisionManager initialized");
|
|
1659
1654
|
}
|
|
@@ -1717,11 +1712,7 @@ var VisionManager = class _VisionManager {
|
|
|
1717
1712
|
* @returns {boolean} - Returns true if cache exists, otherwise returns false.
|
|
1718
1713
|
*/
|
|
1719
1714
|
checkCacheExists(modelId, type) {
|
|
1720
|
-
const modelPath = path4.join(
|
|
1721
|
-
this.modelsDir,
|
|
1722
|
-
modelId.replace("/", "--"),
|
|
1723
|
-
type
|
|
1724
|
-
);
|
|
1715
|
+
const modelPath = path4.join(this.modelsDir, modelId.replace("/", "--"), type);
|
|
1725
1716
|
if (existsSync(modelPath)) {
|
|
1726
1717
|
logger7.info(`${type} found at: ${modelPath}`);
|
|
1727
1718
|
return true;
|
|
@@ -1759,9 +1750,7 @@ var VisionManager = class _VisionManager {
|
|
|
1759
1750
|
* @returns {object} The model configuration object containing device, dtype, and cache_dir.
|
|
1760
1751
|
*/
|
|
1761
1752
|
getModelConfig(componentName) {
|
|
1762
|
-
const component = this.modelComponents.find(
|
|
1763
|
-
(c) => c.name === componentName
|
|
1764
|
-
);
|
|
1753
|
+
const component = this.modelComponents.find((c) => c.name === componentName);
|
|
1765
1754
|
return {
|
|
1766
1755
|
device: this.platformConfig.device,
|
|
1767
1756
|
dtype: component?.dtype || "fp32",
|
|
@@ -1777,9 +1766,7 @@ var VisionManager = class _VisionManager {
|
|
|
1777
1766
|
async initialize() {
|
|
1778
1767
|
try {
|
|
1779
1768
|
if (this.initialized) {
|
|
1780
|
-
logger7.info(
|
|
1781
|
-
"Vision model already initialized, skipping initialization"
|
|
1782
|
-
);
|
|
1769
|
+
logger7.info("Vision model already initialized, skipping initialization");
|
|
1783
1770
|
return;
|
|
1784
1771
|
}
|
|
1785
1772
|
logger7.info("Starting vision model initialization...");
|
|
@@ -1795,32 +1782,25 @@ var VisionManager = class _VisionManager {
|
|
|
1795
1782
|
try {
|
|
1796
1783
|
let lastProgress = -1;
|
|
1797
1784
|
const modelCached = this.checkCacheExists(modelSpec.modelId, "model");
|
|
1798
|
-
const model = await Florence2ForConditionalGeneration.from_pretrained(
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
);
|
|
1815
|
-
const progressBar = "\u25B0".repeat(filledLength) + "\u25B1".repeat(barLength - filledLength);
|
|
1816
|
-
logger7.info(
|
|
1817
|
-
`Downloading vision model: ${progressBar} ${currentProgress}%`
|
|
1818
|
-
);
|
|
1819
|
-
if (currentProgress === 100) this.modelDownloaded = true;
|
|
1820
|
-
}
|
|
1785
|
+
const model = await Florence2ForConditionalGeneration.from_pretrained(modelSpec.modelId, {
|
|
1786
|
+
device: "cpu",
|
|
1787
|
+
cache_dir: this.modelsDir,
|
|
1788
|
+
local_files_only: modelCached,
|
|
1789
|
+
revision: "main",
|
|
1790
|
+
progress_callback: (progressInfo) => {
|
|
1791
|
+
if (modelCached || this.modelDownloaded) return;
|
|
1792
|
+
const progress = "progress" in progressInfo ? Math.max(0, Math.min(1, progressInfo.progress)) : 0;
|
|
1793
|
+
const currentProgress = Math.round(progress * 100);
|
|
1794
|
+
if (currentProgress > lastProgress + 9 || currentProgress === 100) {
|
|
1795
|
+
lastProgress = currentProgress;
|
|
1796
|
+
const barLength = 30;
|
|
1797
|
+
const filledLength = Math.floor(currentProgress / 100 * barLength);
|
|
1798
|
+
const progressBar = "\u25B0".repeat(filledLength) + "\u25B1".repeat(barLength - filledLength);
|
|
1799
|
+
logger7.info(`Downloading vision model: ${progressBar} ${currentProgress}%`);
|
|
1800
|
+
if (currentProgress === 100) this.modelDownloaded = true;
|
|
1821
1801
|
}
|
|
1822
1802
|
}
|
|
1823
|
-
);
|
|
1803
|
+
});
|
|
1824
1804
|
this.model = model;
|
|
1825
1805
|
logger7.success("Florence2 model loaded successfully");
|
|
1826
1806
|
} catch (error) {
|
|
@@ -1833,35 +1813,25 @@ var VisionManager = class _VisionManager {
|
|
|
1833
1813
|
}
|
|
1834
1814
|
logger7.info("Loading vision tokenizer...");
|
|
1835
1815
|
try {
|
|
1836
|
-
const tokenizerCached = this.checkCacheExists(
|
|
1837
|
-
modelSpec.modelId,
|
|
1838
|
-
"tokenizer"
|
|
1839
|
-
);
|
|
1816
|
+
const tokenizerCached = this.checkCacheExists(modelSpec.modelId, "tokenizer");
|
|
1840
1817
|
let tokenizerProgress = -1;
|
|
1841
|
-
this.tokenizer = await AutoTokenizer2.from_pretrained(
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
);
|
|
1856
|
-
const progressBar = "\u25B0".repeat(filledLength) + "\u25B1".repeat(barLength - filledLength);
|
|
1857
|
-
logger7.info(
|
|
1858
|
-
`Downloading vision tokenizer: ${progressBar} ${currentProgress}%`
|
|
1859
|
-
);
|
|
1860
|
-
if (currentProgress === 100) this.tokenizerDownloaded = true;
|
|
1861
|
-
}
|
|
1818
|
+
this.tokenizer = await AutoTokenizer2.from_pretrained(modelSpec.modelId, {
|
|
1819
|
+
cache_dir: this.modelsDir,
|
|
1820
|
+
local_files_only: tokenizerCached,
|
|
1821
|
+
progress_callback: (progressInfo) => {
|
|
1822
|
+
if (tokenizerCached || this.tokenizerDownloaded) return;
|
|
1823
|
+
const progress = "progress" in progressInfo ? Math.max(0, Math.min(1, progressInfo.progress)) : 0;
|
|
1824
|
+
const currentProgress = Math.round(progress * 100);
|
|
1825
|
+
if (currentProgress !== tokenizerProgress) {
|
|
1826
|
+
tokenizerProgress = currentProgress;
|
|
1827
|
+
const barLength = 30;
|
|
1828
|
+
const filledLength = Math.floor(currentProgress / 100 * barLength);
|
|
1829
|
+
const progressBar = "\u25B0".repeat(filledLength) + "\u25B1".repeat(barLength - filledLength);
|
|
1830
|
+
logger7.info(`Downloading vision tokenizer: ${progressBar} ${currentProgress}%`);
|
|
1831
|
+
if (currentProgress === 100) this.tokenizerDownloaded = true;
|
|
1862
1832
|
}
|
|
1863
1833
|
}
|
|
1864
|
-
);
|
|
1834
|
+
});
|
|
1865
1835
|
logger7.success("Vision tokenizer loaded successfully");
|
|
1866
1836
|
} catch (error) {
|
|
1867
1837
|
logger7.error("Failed to load tokenizer:", {
|
|
@@ -1873,36 +1843,26 @@ var VisionManager = class _VisionManager {
|
|
|
1873
1843
|
}
|
|
1874
1844
|
logger7.info("Loading vision processor...");
|
|
1875
1845
|
try {
|
|
1876
|
-
const processorCached = this.checkCacheExists(
|
|
1877
|
-
modelSpec.modelId,
|
|
1878
|
-
"processor"
|
|
1879
|
-
);
|
|
1846
|
+
const processorCached = this.checkCacheExists(modelSpec.modelId, "processor");
|
|
1880
1847
|
let processorProgress = -1;
|
|
1881
|
-
this.processor = await AutoProcessor.from_pretrained(
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
);
|
|
1897
|
-
const progressBar = "\u25B0".repeat(filledLength) + "\u25B1".repeat(barLength - filledLength);
|
|
1898
|
-
logger7.info(
|
|
1899
|
-
`Downloading vision processor: ${progressBar} ${currentProgress}%`
|
|
1900
|
-
);
|
|
1901
|
-
if (currentProgress === 100) this.processorDownloaded = true;
|
|
1902
|
-
}
|
|
1848
|
+
this.processor = await AutoProcessor.from_pretrained(modelSpec.modelId, {
|
|
1849
|
+
device: "cpu",
|
|
1850
|
+
cache_dir: this.modelsDir,
|
|
1851
|
+
local_files_only: processorCached,
|
|
1852
|
+
progress_callback: (progressInfo) => {
|
|
1853
|
+
if (processorCached || this.processorDownloaded) return;
|
|
1854
|
+
const progress = "progress" in progressInfo ? Math.max(0, Math.min(1, progressInfo.progress)) : 0;
|
|
1855
|
+
const currentProgress = Math.round(progress * 100);
|
|
1856
|
+
if (currentProgress !== processorProgress) {
|
|
1857
|
+
processorProgress = currentProgress;
|
|
1858
|
+
const barLength = 30;
|
|
1859
|
+
const filledLength = Math.floor(currentProgress / 100 * barLength);
|
|
1860
|
+
const progressBar = "\u25B0".repeat(filledLength) + "\u25B1".repeat(barLength - filledLength);
|
|
1861
|
+
logger7.info(`Downloading vision processor: ${progressBar} ${currentProgress}%`);
|
|
1862
|
+
if (currentProgress === 100) this.processorDownloaded = true;
|
|
1903
1863
|
}
|
|
1904
1864
|
}
|
|
1905
|
-
);
|
|
1865
|
+
});
|
|
1906
1866
|
logger7.success("Vision processor loaded successfully");
|
|
1907
1867
|
} catch (error) {
|
|
1908
1868
|
logger7.error("Failed to load vision processor:", {
|
|
@@ -2139,14 +2099,8 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
2139
2099
|
*/
|
|
2140
2100
|
_postValidateInit() {
|
|
2141
2101
|
this._setupModelsDir();
|
|
2142
|
-
this.downloadManager = DownloadManager.getInstance(
|
|
2143
|
-
|
|
2144
|
-
this.modelsDir
|
|
2145
|
-
);
|
|
2146
|
-
this.tokenizerManager = TokenizerManager.getInstance(
|
|
2147
|
-
this.cacheDir,
|
|
2148
|
-
this.modelsDir
|
|
2149
|
-
);
|
|
2102
|
+
this.downloadManager = DownloadManager.getInstance(this.cacheDir, this.modelsDir);
|
|
2103
|
+
this.tokenizerManager = TokenizerManager.getInstance(this.cacheDir, this.modelsDir);
|
|
2150
2104
|
this.visionManager = VisionManager.getInstance(this.cacheDir);
|
|
2151
2105
|
this.transcribeManager = TranscribeManager.getInstance(this.cacheDir);
|
|
2152
2106
|
this.ttsManager = TTSManager.getInstance(this.cacheDir);
|
|
@@ -2159,10 +2113,7 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
2159
2113
|
const modelsDirEnv = this.config?.MODELS_DIR?.trim() || process.env.MODELS_DIR?.trim();
|
|
2160
2114
|
if (modelsDirEnv) {
|
|
2161
2115
|
this.modelsDir = path5.resolve(modelsDirEnv);
|
|
2162
|
-
logger8.info(
|
|
2163
|
-
"Using models directory from MODELS_DIR environment variable:",
|
|
2164
|
-
this.modelsDir
|
|
2165
|
-
);
|
|
2116
|
+
logger8.info("Using models directory from MODELS_DIR environment variable:", this.modelsDir);
|
|
2166
2117
|
} else {
|
|
2167
2118
|
this.modelsDir = path5.join(os3.homedir(), ".eliza", "models");
|
|
2168
2119
|
logger8.info(
|
|
@@ -2172,10 +2123,7 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
2172
2123
|
}
|
|
2173
2124
|
if (!fs5.existsSync(this.modelsDir)) {
|
|
2174
2125
|
fs5.mkdirSync(this.modelsDir, { recursive: true });
|
|
2175
|
-
logger8.debug(
|
|
2176
|
-
"Ensured models directory exists (created):",
|
|
2177
|
-
this.modelsDir
|
|
2178
|
-
);
|
|
2126
|
+
logger8.debug("Ensured models directory exists (created):", this.modelsDir);
|
|
2179
2127
|
} else {
|
|
2180
2128
|
logger8.debug("Models directory already exists:", this.modelsDir);
|
|
2181
2129
|
}
|
|
@@ -2188,10 +2136,7 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
2188
2136
|
const cacheDirEnv = this.config?.CACHE_DIR?.trim() || process.env.CACHE_DIR?.trim();
|
|
2189
2137
|
if (cacheDirEnv) {
|
|
2190
2138
|
this.cacheDir = path5.resolve(cacheDirEnv);
|
|
2191
|
-
logger8.info(
|
|
2192
|
-
"Using cache directory from CACHE_DIR environment variable:",
|
|
2193
|
-
this.cacheDir
|
|
2194
|
-
);
|
|
2139
|
+
logger8.info("Using cache directory from CACHE_DIR environment variable:", this.cacheDir);
|
|
2195
2140
|
} else {
|
|
2196
2141
|
const cacheDir = path5.join(os3.homedir(), ".eliza", "cache");
|
|
2197
2142
|
if (!fs5.existsSync(cacheDir)) {
|
|
@@ -2238,24 +2183,12 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
2238
2183
|
logger8.info("Initializing environment configuration...");
|
|
2239
2184
|
this.config = await validateConfig();
|
|
2240
2185
|
this._postValidateInit();
|
|
2241
|
-
this.modelPath = path5.join(
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
);
|
|
2245
|
-
this.mediumModelPath = path5.join(
|
|
2246
|
-
this.modelsDir,
|
|
2247
|
-
this.config.LOCAL_LARGE_MODEL
|
|
2248
|
-
);
|
|
2249
|
-
this.embeddingModelPath = path5.join(
|
|
2250
|
-
this.modelsDir,
|
|
2251
|
-
this.config.LOCAL_EMBEDDING_MODEL
|
|
2252
|
-
);
|
|
2186
|
+
this.modelPath = path5.join(this.modelsDir, this.config.LOCAL_SMALL_MODEL);
|
|
2187
|
+
this.mediumModelPath = path5.join(this.modelsDir, this.config.LOCAL_LARGE_MODEL);
|
|
2188
|
+
this.embeddingModelPath = path5.join(this.modelsDir, this.config.LOCAL_EMBEDDING_MODEL);
|
|
2253
2189
|
logger8.info("Using small model path:", basename(this.modelPath));
|
|
2254
2190
|
logger8.info("Using medium model path:", basename(this.mediumModelPath));
|
|
2255
|
-
logger8.info(
|
|
2256
|
-
"Using embedding model path:",
|
|
2257
|
-
basename(this.embeddingModelPath)
|
|
2258
|
-
);
|
|
2191
|
+
logger8.info("Using embedding model path:", basename(this.embeddingModelPath));
|
|
2259
2192
|
logger8.info("Environment configuration validated and model paths set");
|
|
2260
2193
|
this.environmentInitialized = true;
|
|
2261
2194
|
logger8.success("Environment initialization complete");
|
|
@@ -2293,10 +2226,7 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
2293
2226
|
modelPathToDownload = modelType === ModelType.TEXT_LARGE ? this.mediumModelPath : this.modelPath;
|
|
2294
2227
|
}
|
|
2295
2228
|
try {
|
|
2296
|
-
return await this.downloadManager.downloadModel(
|
|
2297
|
-
modelSpec,
|
|
2298
|
-
modelPathToDownload
|
|
2299
|
-
);
|
|
2229
|
+
return await this.downloadManager.downloadModel(modelSpec, modelPathToDownload);
|
|
2300
2230
|
} catch (error) {
|
|
2301
2231
|
logger8.error("Model download failed:", {
|
|
2302
2232
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -2351,10 +2281,7 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
2351
2281
|
logger8.info("Initializing embedding model...");
|
|
2352
2282
|
logger8.info("Models directory:", this.modelsDir);
|
|
2353
2283
|
if (!fs5.existsSync(this.modelsDir)) {
|
|
2354
|
-
logger8.warn(
|
|
2355
|
-
"Models directory does not exist, creating it:",
|
|
2356
|
-
this.modelsDir
|
|
2357
|
-
);
|
|
2284
|
+
logger8.warn("Models directory does not exist, creating it:", this.modelsDir);
|
|
2358
2285
|
fs5.mkdirSync(this.modelsDir, { recursive: true });
|
|
2359
2286
|
}
|
|
2360
2287
|
await this.downloadModel(ModelType.TEXT_EMBEDDING);
|
|
@@ -2508,10 +2435,7 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
2508
2435
|
runtime: !!params.runtime,
|
|
2509
2436
|
stopSequences: params.stopSequences
|
|
2510
2437
|
});
|
|
2511
|
-
const tokens = await this.tokenizerManager.encode(
|
|
2512
|
-
params.prompt,
|
|
2513
|
-
this.activeModelConfig
|
|
2514
|
-
);
|
|
2438
|
+
const tokens = await this.tokenizerManager.encode(params.prompt, this.activeModelConfig);
|
|
2515
2439
|
logger8.info("Input tokens:", { count: tokens.length });
|
|
2516
2440
|
const systemMessage = "You are a helpful AI assistant. Respond to the current request only.";
|
|
2517
2441
|
await this.chatSession.prompt(systemMessage, {
|
|
@@ -2701,9 +2625,7 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
2701
2625
|
try {
|
|
2702
2626
|
await this.initializeEnvironment();
|
|
2703
2627
|
if (!this.transcribeManager) {
|
|
2704
|
-
this.transcribeManager = TranscribeManager.getInstance(
|
|
2705
|
-
this.cacheDir
|
|
2706
|
-
);
|
|
2628
|
+
this.transcribeManager = TranscribeManager.getInstance(this.cacheDir);
|
|
2707
2629
|
}
|
|
2708
2630
|
const ffmpegReady = await this.transcribeManager.ensureFFmpeg();
|
|
2709
2631
|
if (!ffmpegReady) {
|
|
@@ -2715,9 +2637,7 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
2715
2637
|
);
|
|
2716
2638
|
}
|
|
2717
2639
|
this.transcriptionInitialized = true;
|
|
2718
|
-
logger8.info(
|
|
2719
|
-
"Transcription prerequisites (FFmpeg) checked and ready."
|
|
2720
|
-
);
|
|
2640
|
+
logger8.info("Transcription prerequisites (FFmpeg) checked and ready.");
|
|
2721
2641
|
logger8.info("Transcription model initialized successfully");
|
|
2722
2642
|
} catch (error) {
|
|
2723
2643
|
logger8.error("Failed to initialize transcription model:", error);
|
|
@@ -2800,9 +2720,7 @@ var localAiPlugin = {
|
|
|
2800
2720
|
const text = params?.text;
|
|
2801
2721
|
try {
|
|
2802
2722
|
if (!text) {
|
|
2803
|
-
logger8.debug(
|
|
2804
|
-
"Null or empty text input for embedding, returning zero vector"
|
|
2805
|
-
);
|
|
2723
|
+
logger8.debug("Null or empty text input for embedding, returning zero vector");
|
|
2806
2724
|
return new Array(384).fill(0);
|
|
2807
2725
|
}
|
|
2808
2726
|
return await localAIManager.generateEmbedding(text);
|
|
@@ -2854,13 +2772,8 @@ var localAiPlugin = {
|
|
|
2854
2772
|
try {
|
|
2855
2773
|
jsonObject = JSON.parse(extractedJsonText);
|
|
2856
2774
|
} catch (parseError) {
|
|
2857
|
-
logger8.debug(
|
|
2858
|
-
|
|
2859
|
-
);
|
|
2860
|
-
const fixedJson = extractedJsonText.replace(/:\s*"([^"]*)(?:\n)([^"]*)"/g, ': "$1\\n$2"').replace(
|
|
2861
|
-
/"([^"]*?)[^a-zA-Z0-9\s\.,;:\-_\(\)"'\[\]{}]([^"]*?)"/g,
|
|
2862
|
-
'"$1$2"'
|
|
2863
|
-
).replace(/(\s*)(\w+)(\s*):/g, '$1"$2"$3:').replace(/,(\s*[\]}])/g, "$1");
|
|
2775
|
+
logger8.debug("Initial JSON parse failed, attempting to fix common issues");
|
|
2776
|
+
const fixedJson = extractedJsonText.replace(/:\s*"([^"]*)(?:\n)([^"]*)"/g, ': "$1\\n$2"').replace(/"([^"]*?)[^a-zA-Z0-9\s\.,;:\-_\(\)"'\[\]{}]([^"]*?)"/g, '"$1$2"').replace(/(\s*)(\w+)(\s*):/g, '$1"$2"$3:').replace(/,(\s*[\]}])/g, "$1");
|
|
2864
2777
|
try {
|
|
2865
2778
|
jsonObject = JSON.parse(fixedJson);
|
|
2866
2779
|
} catch (finalError) {
|
|
@@ -2932,13 +2845,8 @@ var localAiPlugin = {
|
|
|
2932
2845
|
try {
|
|
2933
2846
|
jsonObject = JSON.parse(cleanedJsonText);
|
|
2934
2847
|
} catch (parseError) {
|
|
2935
|
-
logger8.debug(
|
|
2936
|
-
|
|
2937
|
-
);
|
|
2938
|
-
const fixedJson = cleanedJsonText.replace(/:\s*"([^"]*)(?:\n)([^"]*)"/g, ': "$1\\n$2"').replace(
|
|
2939
|
-
/"([^"]*?)[^a-zA-Z0-9\s\.,;:\-_\(\)"'\[\]{}]([^"]*?)"/g,
|
|
2940
|
-
'"$1$2"'
|
|
2941
|
-
).replace(/(\s*)(\w+)(\s*):/g, '$1"$2"$3:').replace(/,(\s*[\]}])/g, "$1");
|
|
2848
|
+
logger8.debug("Initial JSON parse failed, attempting to fix common issues");
|
|
2849
|
+
const fixedJson = cleanedJsonText.replace(/:\s*"([^"]*)(?:\n)([^"]*)"/g, ': "$1\\n$2"').replace(/"([^"]*?)[^a-zA-Z0-9\s\.,;:\-_\(\)"'\[\]{}]([^"]*?)"/g, '"$1$2"').replace(/(\s*)(\w+)(\s*):/g, '$1"$2"$3:').replace(/,(\s*[\]}])/g, "$1");
|
|
2942
2850
|
try {
|
|
2943
2851
|
jsonObject = JSON.parse(fixedJson);
|
|
2944
2852
|
} catch (finalError) {
|
|
@@ -3093,16 +3001,10 @@ var localAiPlugin = {
|
|
|
3093
3001
|
fn: async (runtime) => {
|
|
3094
3002
|
try {
|
|
3095
3003
|
logger8.info("Starting TEXT_EMBEDDING test");
|
|
3096
|
-
const embedding = await runtime.useModel(
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
}
|
|
3101
|
-
);
|
|
3102
|
-
logger8.info(
|
|
3103
|
-
"Embedding generated with dimensions:",
|
|
3104
|
-
embedding.length
|
|
3105
|
-
);
|
|
3004
|
+
const embedding = await runtime.useModel(ModelType.TEXT_EMBEDDING, {
|
|
3005
|
+
text: "This is a test of the text embedding model."
|
|
3006
|
+
});
|
|
3007
|
+
logger8.info("Embedding generated with dimensions:", embedding.length);
|
|
3106
3008
|
if (!Array.isArray(embedding)) {
|
|
3107
3009
|
throw new Error("Embedding is not an array");
|
|
3108
3010
|
}
|
|
@@ -3112,10 +3014,7 @@ var localAiPlugin = {
|
|
|
3112
3014
|
if (embedding.some((val) => typeof val !== "number")) {
|
|
3113
3015
|
throw new Error("Embedding contains non-numeric values");
|
|
3114
3016
|
}
|
|
3115
|
-
const nullEmbedding = await runtime.useModel(
|
|
3116
|
-
ModelType.TEXT_EMBEDDING,
|
|
3117
|
-
null
|
|
3118
|
-
);
|
|
3017
|
+
const nullEmbedding = await runtime.useModel(ModelType.TEXT_EMBEDDING, null);
|
|
3119
3018
|
if (!Array.isArray(nullEmbedding) || nullEmbedding.some((val) => val !== 0)) {
|
|
3120
3019
|
throw new Error("Null input did not return zero vector");
|
|
3121
3020
|
}
|
|
@@ -3135,10 +3034,7 @@ var localAiPlugin = {
|
|
|
3135
3034
|
try {
|
|
3136
3035
|
logger8.info("Starting TEXT_TOKENIZER_ENCODE test");
|
|
3137
3036
|
const text = "Hello tokenizer test!";
|
|
3138
|
-
const tokens = await runtime.useModel(
|
|
3139
|
-
ModelType.TEXT_TOKENIZER_ENCODE,
|
|
3140
|
-
{ text }
|
|
3141
|
-
);
|
|
3037
|
+
const tokens = await runtime.useModel(ModelType.TEXT_TOKENIZER_ENCODE, { text });
|
|
3142
3038
|
logger8.info("Encoded tokens:", { count: tokens.length });
|
|
3143
3039
|
if (!Array.isArray(tokens)) {
|
|
3144
3040
|
throw new Error("Tokens output is not an array");
|
|
@@ -3149,9 +3045,7 @@ var localAiPlugin = {
|
|
|
3149
3045
|
if (tokens.some((token) => !Number.isInteger(token))) {
|
|
3150
3046
|
throw new Error("Tokens contain non-integer values");
|
|
3151
3047
|
}
|
|
3152
|
-
logger8.success(
|
|
3153
|
-
"TEXT_TOKENIZER_ENCODE test completed successfully"
|
|
3154
|
-
);
|
|
3048
|
+
logger8.success("TEXT_TOKENIZER_ENCODE test completed successfully");
|
|
3155
3049
|
} catch (error) {
|
|
3156
3050
|
logger8.error("TEXT_TOKENIZER_ENCODE test failed:", {
|
|
3157
3051
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -3167,18 +3061,12 @@ var localAiPlugin = {
|
|
|
3167
3061
|
try {
|
|
3168
3062
|
logger8.info("Starting TEXT_TOKENIZER_DECODE test");
|
|
3169
3063
|
const originalText = "Hello tokenizer test!";
|
|
3170
|
-
const tokens = await runtime.useModel(
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
);
|
|
3176
|
-
const decodedText = await runtime.useModel(
|
|
3177
|
-
ModelType.TEXT_TOKENIZER_DECODE,
|
|
3178
|
-
{
|
|
3179
|
-
tokens
|
|
3180
|
-
}
|
|
3181
|
-
);
|
|
3064
|
+
const tokens = await runtime.useModel(ModelType.TEXT_TOKENIZER_ENCODE, {
|
|
3065
|
+
text: originalText
|
|
3066
|
+
});
|
|
3067
|
+
const decodedText = await runtime.useModel(ModelType.TEXT_TOKENIZER_DECODE, {
|
|
3068
|
+
tokens
|
|
3069
|
+
});
|
|
3182
3070
|
logger8.info("Round trip tokenization:", {
|
|
3183
3071
|
original: originalText,
|
|
3184
3072
|
decoded: decodedText
|
|
@@ -3186,9 +3074,7 @@ var localAiPlugin = {
|
|
|
3186
3074
|
if (typeof decodedText !== "string") {
|
|
3187
3075
|
throw new Error("Decoded output is not a string");
|
|
3188
3076
|
}
|
|
3189
|
-
logger8.success(
|
|
3190
|
-
"TEXT_TOKENIZER_DECODE test completed successfully"
|
|
3191
|
-
);
|
|
3077
|
+
logger8.success("TEXT_TOKENIZER_DECODE test completed successfully");
|
|
3192
3078
|
} catch (error) {
|
|
3193
3079
|
logger8.error("TEXT_TOKENIZER_DECODE test failed:", {
|
|
3194
3080
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -3203,11 +3089,8 @@ var localAiPlugin = {
|
|
|
3203
3089
|
fn: async (runtime) => {
|
|
3204
3090
|
try {
|
|
3205
3091
|
logger8.info("Starting IMAGE_DESCRIPTION test");
|
|
3206
|
-
const imageUrl = "https://
|
|
3207
|
-
const result = await runtime.useModel(
|
|
3208
|
-
ModelType.IMAGE_DESCRIPTION,
|
|
3209
|
-
imageUrl
|
|
3210
|
-
);
|
|
3092
|
+
const imageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/320px-Cat03.jpg";
|
|
3093
|
+
const result = await runtime.useModel(ModelType.IMAGE_DESCRIPTION, imageUrl);
|
|
3211
3094
|
logger8.info("Image description result:", result);
|
|
3212
3095
|
if (!result || typeof result !== "object") {
|
|
3213
3096
|
throw new Error("Invalid response format");
|
|
@@ -3233,37 +3116,37 @@ var localAiPlugin = {
|
|
|
3233
3116
|
fn: async (runtime) => {
|
|
3234
3117
|
try {
|
|
3235
3118
|
logger8.info("Starting TRANSCRIPTION test");
|
|
3236
|
-
const
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
);
|
|
3119
|
+
const channels = 1;
|
|
3120
|
+
const sampleRate = 16e3;
|
|
3121
|
+
const bitsPerSample = 16;
|
|
3122
|
+
const duration = 0.5;
|
|
3123
|
+
const numSamples = Math.floor(sampleRate * duration);
|
|
3124
|
+
const dataSize = numSamples * channels * (bitsPerSample / 8);
|
|
3125
|
+
const buffer = Buffer.alloc(44 + dataSize);
|
|
3126
|
+
buffer.write("RIFF", 0);
|
|
3127
|
+
buffer.writeUInt32LE(36 + dataSize, 4);
|
|
3128
|
+
buffer.write("WAVE", 8);
|
|
3129
|
+
buffer.write("fmt ", 12);
|
|
3130
|
+
buffer.writeUInt32LE(16, 16);
|
|
3131
|
+
buffer.writeUInt16LE(1, 20);
|
|
3132
|
+
buffer.writeUInt16LE(channels, 22);
|
|
3133
|
+
buffer.writeUInt32LE(sampleRate, 24);
|
|
3134
|
+
buffer.writeUInt32LE(sampleRate * channels * (bitsPerSample / 8), 28);
|
|
3135
|
+
buffer.writeUInt16LE(channels * (bitsPerSample / 8), 32);
|
|
3136
|
+
buffer.writeUInt16LE(bitsPerSample, 34);
|
|
3137
|
+
buffer.write("data", 36);
|
|
3138
|
+
buffer.writeUInt32LE(dataSize, 40);
|
|
3139
|
+
const frequency = 440;
|
|
3140
|
+
for (let i = 0; i < numSamples; i++) {
|
|
3141
|
+
const sample = Math.sin(2 * Math.PI * frequency * i / sampleRate) * 0.1 * 32767;
|
|
3142
|
+
buffer.writeInt16LE(Math.floor(sample), 44 + i * 2);
|
|
3143
|
+
}
|
|
3144
|
+
const transcription = await runtime.useModel(ModelType.TRANSCRIPTION, buffer);
|
|
3263
3145
|
logger8.info("Transcription result:", transcription);
|
|
3264
3146
|
if (typeof transcription !== "string") {
|
|
3265
3147
|
throw new Error("Transcription result is not a string");
|
|
3266
3148
|
}
|
|
3149
|
+
logger8.info("Transcription completed (may be empty for non-speech audio)");
|
|
3267
3150
|
logger8.success("TRANSCRIPTION test completed successfully");
|
|
3268
3151
|
} catch (error) {
|
|
3269
3152
|
logger8.error("TRANSCRIPTION test failed:", {
|
|
@@ -3280,10 +3163,7 @@ var localAiPlugin = {
|
|
|
3280
3163
|
try {
|
|
3281
3164
|
logger8.info("Starting TEXT_TO_SPEECH test");
|
|
3282
3165
|
const testText = "This is a test of the text to speech system.";
|
|
3283
|
-
const audioStream = await runtime.useModel(
|
|
3284
|
-
ModelType.TEXT_TO_SPEECH,
|
|
3285
|
-
testText
|
|
3286
|
-
);
|
|
3166
|
+
const audioStream = await runtime.useModel(ModelType.TEXT_TO_SPEECH, testText);
|
|
3287
3167
|
if (!(audioStream instanceof Readable2)) {
|
|
3288
3168
|
throw new Error("TTS output is not a readable stream");
|
|
3289
3169
|
}
|