@elizaos/plugin-local-ai 1.0.0-alpha.63 → 1.0.0-alpha.65
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/README.md +27 -28
- package/dist/index.js +320 -363
- package/dist/index.js.map +1 -1
- package/package.json +68 -63
package/dist/index.js
CHANGED
|
@@ -3,10 +3,7 @@ import fs5 from "node:fs";
|
|
|
3
3
|
import path5 from "node:path";
|
|
4
4
|
import { Readable as Readable2 } from "node:stream";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import {
|
|
7
|
-
ModelType as ModelType3,
|
|
8
|
-
logger as logger10
|
|
9
|
-
} from "@elizaos/core";
|
|
6
|
+
import { ModelType as ModelType3, logger as logger10 } from "@elizaos/core";
|
|
10
7
|
import { EmbeddingModel, FlagEmbedding } from "fastembed";
|
|
11
8
|
import {
|
|
12
9
|
LlamaChatSession,
|
|
@@ -45,9 +42,7 @@ function validateModelConfig(config) {
|
|
|
45
42
|
logger.info("Setting USE_LOCAL_AI to true as it's required");
|
|
46
43
|
}
|
|
47
44
|
if (config.USE_STUDIOLM_TEXT_MODELS && config.USE_OLLAMA_TEXT_MODELS) {
|
|
48
|
-
throw new Error(
|
|
49
|
-
"StudioLM and Ollama text models cannot be enabled simultaneously"
|
|
50
|
-
);
|
|
45
|
+
throw new Error("StudioLM and Ollama text models cannot be enabled simultaneously");
|
|
51
46
|
}
|
|
52
47
|
logger.info("Configuration is valid");
|
|
53
48
|
}
|
|
@@ -94,24 +89,24 @@ ${errorMessages}`);
|
|
|
94
89
|
// src/types.ts
|
|
95
90
|
var MODEL_SPECS = {
|
|
96
91
|
small: {
|
|
97
|
-
name: "
|
|
98
|
-
repo: "
|
|
99
|
-
size: "
|
|
100
|
-
quantization: "
|
|
92
|
+
name: "DeepHermes-3-Llama-3-3B-Preview-q4.gguf",
|
|
93
|
+
repo: "NousResearch/DeepHermes-3-Llama-3-3B-Preview-GGUF",
|
|
94
|
+
size: "3B",
|
|
95
|
+
quantization: "Q4_0",
|
|
101
96
|
contextSize: 8192,
|
|
102
97
|
tokenizer: {
|
|
103
|
-
name: "
|
|
98
|
+
name: "NousResearch/DeepHermes-3-Llama-3-3B-Preview",
|
|
104
99
|
type: "llama"
|
|
105
100
|
}
|
|
106
101
|
},
|
|
107
102
|
medium: {
|
|
108
|
-
name: "
|
|
109
|
-
repo: "
|
|
110
|
-
size: "
|
|
111
|
-
quantization: "
|
|
103
|
+
name: "DeepHermes-3-Llama-3-8B-q4.gguf",
|
|
104
|
+
repo: "NousResearch/DeepHermes-3-Llama-3-8B-Preview-GGUF",
|
|
105
|
+
size: "8B",
|
|
106
|
+
quantization: "Q4_0",
|
|
112
107
|
contextSize: 8192,
|
|
113
108
|
tokenizer: {
|
|
114
|
-
name: "
|
|
109
|
+
name: "NousResearch/DeepHermes-3-Llama-3-8B-Preview",
|
|
115
110
|
type: "llama"
|
|
116
111
|
}
|
|
117
112
|
},
|
|
@@ -159,12 +154,7 @@ var MODEL_SPECS = {
|
|
|
159
154
|
quantization: "Q8_0",
|
|
160
155
|
speakers: ["male_1", "male_2", "female_1", "female_2"],
|
|
161
156
|
languages: ["en"],
|
|
162
|
-
features: [
|
|
163
|
-
"MULTI_SPEAKER",
|
|
164
|
-
"VOICE_CLONING",
|
|
165
|
-
"EMOTION_CONTROL",
|
|
166
|
-
"SPEED_CONTROL"
|
|
167
|
-
],
|
|
157
|
+
features: ["MULTI_SPEAKER", "VOICE_CLONING", "EMOTION_CONTROL", "SPEED_CONTROL"],
|
|
168
158
|
maxInputLength: 4096,
|
|
169
159
|
sampleRate: 24e3,
|
|
170
160
|
contextSize: 2048,
|
|
@@ -178,14 +168,7 @@ var MODEL_SPECS = {
|
|
|
178
168
|
repo: "OuteAI/OuteTTS-0.3-1B-GGUF",
|
|
179
169
|
size: "1B",
|
|
180
170
|
quantization: "Q8_0",
|
|
181
|
-
speakers: [
|
|
182
|
-
"male_1",
|
|
183
|
-
"male_2",
|
|
184
|
-
"male_3",
|
|
185
|
-
"female_1",
|
|
186
|
-
"female_2",
|
|
187
|
-
"female_3"
|
|
188
|
-
],
|
|
171
|
+
speakers: ["male_1", "male_2", "male_3", "female_1", "female_2", "female_3"],
|
|
189
172
|
languages: ["en", "es", "fr", "de", "it"],
|
|
190
173
|
features: [
|
|
191
174
|
"MULTI_SPEAKER",
|
|
@@ -218,20 +201,7 @@ var MODEL_SPECS = {
|
|
|
218
201
|
"female_3",
|
|
219
202
|
"female_4"
|
|
220
203
|
],
|
|
221
|
-
languages: [
|
|
222
|
-
"en",
|
|
223
|
-
"es",
|
|
224
|
-
"fr",
|
|
225
|
-
"de",
|
|
226
|
-
"it",
|
|
227
|
-
"pt",
|
|
228
|
-
"nl",
|
|
229
|
-
"pl",
|
|
230
|
-
"ru",
|
|
231
|
-
"ja",
|
|
232
|
-
"ko",
|
|
233
|
-
"zh"
|
|
234
|
-
],
|
|
204
|
+
languages: ["en", "es", "fr", "de", "it", "pt", "nl", "pl", "ru", "ja", "ko", "zh"],
|
|
235
205
|
features: [
|
|
236
206
|
"MULTI_SPEAKER",
|
|
237
207
|
"VOICE_CLONING",
|
|
@@ -355,10 +325,7 @@ var DownloadManager = class _DownloadManager {
|
|
|
355
325
|
reject(new Error(`Failed to download: ${response.statusCode}`));
|
|
356
326
|
return;
|
|
357
327
|
}
|
|
358
|
-
const totalSize = Number.parseInt(
|
|
359
|
-
response.headers["content-length"] || "0",
|
|
360
|
-
10
|
|
361
|
-
);
|
|
328
|
+
const totalSize = Number.parseInt(response.headers["content-length"] || "0", 10);
|
|
362
329
|
let downloadedSize = 0;
|
|
363
330
|
let lastLoggedPercent = 0;
|
|
364
331
|
const barLength = 30;
|
|
@@ -369,13 +336,9 @@ var DownloadManager = class _DownloadManager {
|
|
|
369
336
|
downloadedSize += chunk.length;
|
|
370
337
|
const percent = Math.round(downloadedSize / totalSize * 100);
|
|
371
338
|
if (percent >= lastLoggedPercent + 5) {
|
|
372
|
-
const filledLength = Math.floor(
|
|
373
|
-
downloadedSize / totalSize * barLength
|
|
374
|
-
);
|
|
339
|
+
const filledLength = Math.floor(downloadedSize / totalSize * barLength);
|
|
375
340
|
const progressBar = "\u25B0".repeat(filledLength) + "\u25B1".repeat(barLength - filledLength);
|
|
376
|
-
logger2.info(
|
|
377
|
-
`Downloading ${fileName}: ${progressBar} ${percent}%`
|
|
378
|
-
);
|
|
341
|
+
logger2.info(`Downloading ${fileName}: ${progressBar} ${percent}%`);
|
|
379
342
|
lastLoggedPercent = percent;
|
|
380
343
|
}
|
|
381
344
|
});
|
|
@@ -390,24 +353,18 @@ var DownloadManager = class _DownloadManager {
|
|
|
390
353
|
fs.mkdirSync(destDir, { recursive: true });
|
|
391
354
|
}
|
|
392
355
|
if (!fs.existsSync(tempPath)) {
|
|
393
|
-
reject(
|
|
394
|
-
new Error(`Temporary file ${tempPath} does not exist`)
|
|
395
|
-
);
|
|
356
|
+
reject(new Error(`Temporary file ${tempPath} does not exist`));
|
|
396
357
|
return;
|
|
397
358
|
}
|
|
398
359
|
if (fs.existsSync(destPath)) {
|
|
399
360
|
try {
|
|
400
361
|
const backupPath = `${destPath}.bak`;
|
|
401
362
|
fs.renameSync(destPath, backupPath);
|
|
402
|
-
logger2.info(
|
|
403
|
-
`Created backup of existing file: ${backupPath}`
|
|
404
|
-
);
|
|
363
|
+
logger2.info(`Created backup of existing file: ${backupPath}`);
|
|
405
364
|
fs.renameSync(tempPath, destPath);
|
|
406
365
|
if (fs.existsSync(backupPath)) {
|
|
407
366
|
fs.unlinkSync(backupPath);
|
|
408
|
-
logger2.info(
|
|
409
|
-
`Removed backup file after successful update: ${backupPath}`
|
|
410
|
-
);
|
|
367
|
+
logger2.info(`Removed backup file after successful update: ${backupPath}`);
|
|
411
368
|
}
|
|
412
369
|
} catch (moveErr) {
|
|
413
370
|
logger2.error(
|
|
@@ -417,9 +374,7 @@ var DownloadManager = class _DownloadManager {
|
|
|
417
374
|
if (fs.existsSync(backupPath)) {
|
|
418
375
|
try {
|
|
419
376
|
fs.renameSync(backupPath, destPath);
|
|
420
|
-
logger2.info(
|
|
421
|
-
`Restored from backup after failed update: ${backupPath}`
|
|
422
|
-
);
|
|
377
|
+
logger2.info(`Restored from backup after failed update: ${backupPath}`);
|
|
423
378
|
} catch (restoreErr) {
|
|
424
379
|
logger2.error(
|
|
425
380
|
`Failed to restore from backup: ${restoreErr instanceof Error ? restoreErr.message : String(restoreErr)}`
|
|
@@ -441,9 +396,7 @@ var DownloadManager = class _DownloadManager {
|
|
|
441
396
|
} else {
|
|
442
397
|
fs.renameSync(tempPath, destPath);
|
|
443
398
|
}
|
|
444
|
-
logger2.success(
|
|
445
|
-
`Download of ${fileName} completed successfully`
|
|
446
|
-
);
|
|
399
|
+
logger2.success(`Download of ${fileName} completed successfully`);
|
|
447
400
|
this.activeDownloads.delete(destPath);
|
|
448
401
|
resolve();
|
|
449
402
|
} catch (err) {
|
|
@@ -465,9 +418,7 @@ var DownloadManager = class _DownloadManager {
|
|
|
465
418
|
});
|
|
466
419
|
});
|
|
467
420
|
file.on("error", (err) => {
|
|
468
|
-
logger2.error(
|
|
469
|
-
`File write error: ${err instanceof Error ? err.message : String(err)}`
|
|
470
|
-
);
|
|
421
|
+
logger2.error(`File write error: ${err instanceof Error ? err.message : String(err)}`);
|
|
471
422
|
file.close(() => {
|
|
472
423
|
if (fs.existsSync(tempPath)) {
|
|
473
424
|
try {
|
|
@@ -485,9 +436,7 @@ var DownloadManager = class _DownloadManager {
|
|
|
485
436
|
}
|
|
486
437
|
);
|
|
487
438
|
request.on("error", (err) => {
|
|
488
|
-
logger2.error(
|
|
489
|
-
`Request error: ${err instanceof Error ? err.message : String(err)}`
|
|
490
|
-
);
|
|
439
|
+
logger2.error(`Request error: ${err instanceof Error ? err.message : String(err)}`);
|
|
491
440
|
if (fs.existsSync(tempPath)) {
|
|
492
441
|
try {
|
|
493
442
|
fs.unlinkSync(tempPath);
|
|
@@ -526,9 +475,7 @@ var DownloadManager = class _DownloadManager {
|
|
|
526
475
|
*/
|
|
527
476
|
async downloadFile(url, destPath) {
|
|
528
477
|
if (this.activeDownloads.has(destPath)) {
|
|
529
|
-
logger2.info(
|
|
530
|
-
`Download for ${destPath} already in progress, waiting for it to complete...`
|
|
531
|
-
);
|
|
478
|
+
logger2.info(`Download for ${destPath} already in progress, waiting for it to complete...`);
|
|
532
479
|
const existingDownload = this.activeDownloads.get(destPath);
|
|
533
480
|
if (existingDownload) {
|
|
534
481
|
return existingDownload;
|
|
@@ -866,9 +813,7 @@ var OllamaManager = class _OllamaManager {
|
|
|
866
813
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
867
814
|
});
|
|
868
815
|
if (!this.initialized && !isInitialized) {
|
|
869
|
-
throw new Error(
|
|
870
|
-
"Ollama not initialized. Please initialize before generating text."
|
|
871
|
-
);
|
|
816
|
+
throw new Error("Ollama not initialized. Please initialize before generating text.");
|
|
872
817
|
}
|
|
873
818
|
logger3.info("Ollama preparing request:", {
|
|
874
819
|
model: params.modelType === ModelType.TEXT_LARGE ? this.configuredModels.medium : this.configuredModels.small,
|
|
@@ -1046,9 +991,7 @@ var PlatformManager = class _PlatformManager {
|
|
|
1046
991
|
isAppleSilicon: true
|
|
1047
992
|
};
|
|
1048
993
|
}
|
|
1049
|
-
const { stdout: gpuInfo } = await execAsync(
|
|
1050
|
-
"system_profiler SPDisplaysDataType"
|
|
1051
|
-
);
|
|
994
|
+
const { stdout: gpuInfo } = await execAsync("system_profiler SPDisplaysDataType");
|
|
1052
995
|
return {
|
|
1053
996
|
name: gpuInfo.split("Chipset Model:")[1]?.split("\n")[0]?.trim() || "Unknown GPU",
|
|
1054
997
|
type: "metal",
|
|
@@ -1070,9 +1013,7 @@ var PlatformManager = class _PlatformManager {
|
|
|
1070
1013
|
*/
|
|
1071
1014
|
async detectWindowsGPU() {
|
|
1072
1015
|
try {
|
|
1073
|
-
const { stdout } = await execAsync(
|
|
1074
|
-
"wmic path win32_VideoController get name"
|
|
1075
|
-
);
|
|
1016
|
+
const { stdout } = await execAsync("wmic path win32_VideoController get name");
|
|
1076
1017
|
const gpuName = stdout.split("\n")[1].trim();
|
|
1077
1018
|
if (gpuName.toLowerCase().includes("nvidia")) {
|
|
1078
1019
|
const { stdout: nvidiaInfo } = await execAsync(
|
|
@@ -1483,9 +1424,7 @@ var StudioLMManager = class _StudioLMManager {
|
|
|
1483
1424
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1484
1425
|
});
|
|
1485
1426
|
if (!this.initialized && !isInitialized) {
|
|
1486
|
-
throw new Error(
|
|
1487
|
-
"StudioLM not initialized. Please initialize before generating text."
|
|
1488
|
-
);
|
|
1427
|
+
throw new Error("StudioLM not initialized. Please initialize before generating text.");
|
|
1489
1428
|
}
|
|
1490
1429
|
const messages = [
|
|
1491
1430
|
{
|
|
@@ -1557,9 +1496,7 @@ var StudioLMManager = class _StudioLMManager {
|
|
|
1557
1496
|
|
|
1558
1497
|
// src/utils/tokenizerManager.ts
|
|
1559
1498
|
import { logger as logger6 } from "@elizaos/core";
|
|
1560
|
-
import {
|
|
1561
|
-
AutoTokenizer
|
|
1562
|
-
} from "@huggingface/transformers";
|
|
1499
|
+
import { AutoTokenizer } from "@huggingface/transformers";
|
|
1563
1500
|
var TokenizerManager = class _TokenizerManager {
|
|
1564
1501
|
static instance = null;
|
|
1565
1502
|
tokenizers;
|
|
@@ -1609,18 +1546,13 @@ var TokenizerManager = class _TokenizerManager {
|
|
|
1609
1546
|
logger6.info("Using cached tokenizer:", { key: tokenizerKey });
|
|
1610
1547
|
const cachedTokenizer = this.tokenizers.get(tokenizerKey);
|
|
1611
1548
|
if (!cachedTokenizer) {
|
|
1612
|
-
throw new Error(
|
|
1613
|
-
`Tokenizer ${tokenizerKey} exists in map but returned undefined`
|
|
1614
|
-
);
|
|
1549
|
+
throw new Error(`Tokenizer ${tokenizerKey} exists in map but returned undefined`);
|
|
1615
1550
|
}
|
|
1616
1551
|
return cachedTokenizer;
|
|
1617
1552
|
}
|
|
1618
1553
|
const fs6 = await import("node:fs");
|
|
1619
1554
|
if (!fs6.existsSync(this.modelsDir)) {
|
|
1620
|
-
logger6.warn(
|
|
1621
|
-
"Models directory does not exist, creating it:",
|
|
1622
|
-
this.modelsDir
|
|
1623
|
-
);
|
|
1555
|
+
logger6.warn("Models directory does not exist, creating it:", this.modelsDir);
|
|
1624
1556
|
fs6.mkdirSync(this.modelsDir, { recursive: true });
|
|
1625
1557
|
}
|
|
1626
1558
|
logger6.info(
|
|
@@ -1628,13 +1560,10 @@ var TokenizerManager = class _TokenizerManager {
|
|
|
1628
1560
|
this.modelsDir
|
|
1629
1561
|
);
|
|
1630
1562
|
try {
|
|
1631
|
-
const tokenizer = await AutoTokenizer.from_pretrained(
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
local_files_only: false
|
|
1636
|
-
}
|
|
1637
|
-
);
|
|
1563
|
+
const tokenizer = await AutoTokenizer.from_pretrained(modelConfig.tokenizer.name, {
|
|
1564
|
+
cache_dir: this.modelsDir,
|
|
1565
|
+
local_files_only: false
|
|
1566
|
+
});
|
|
1638
1567
|
this.tokenizers.set(tokenizerKey, tokenizer);
|
|
1639
1568
|
logger6.success("Tokenizer loaded successfully:", { key: tokenizerKey });
|
|
1640
1569
|
return tokenizer;
|
|
@@ -1646,13 +1575,10 @@ var TokenizerManager = class _TokenizerManager {
|
|
|
1646
1575
|
modelsDir: this.modelsDir
|
|
1647
1576
|
});
|
|
1648
1577
|
logger6.info("Retrying tokenizer loading...");
|
|
1649
|
-
const tokenizer = await AutoTokenizer.from_pretrained(
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
local_files_only: false
|
|
1654
|
-
}
|
|
1655
|
-
);
|
|
1578
|
+
const tokenizer = await AutoTokenizer.from_pretrained(modelConfig.tokenizer.name, {
|
|
1579
|
+
cache_dir: this.modelsDir,
|
|
1580
|
+
local_files_only: false
|
|
1581
|
+
});
|
|
1656
1582
|
this.tokenizers.set(tokenizerKey, tokenizer);
|
|
1657
1583
|
logger6.success("Tokenizer loaded successfully on retry:", {
|
|
1658
1584
|
key: tokenizerKey
|
|
@@ -1871,9 +1797,7 @@ var TranscribeManager = class _TranscribeManager {
|
|
|
1871
1797
|
*/
|
|
1872
1798
|
async checkFFmpegAvailability() {
|
|
1873
1799
|
try {
|
|
1874
|
-
const { stdout, stderr } = await execAsync2(
|
|
1875
|
-
"which ffmpeg || where ffmpeg"
|
|
1876
|
-
);
|
|
1800
|
+
const { stdout, stderr } = await execAsync2("which ffmpeg || where ffmpeg");
|
|
1877
1801
|
this.ffmpegPath = stdout.trim();
|
|
1878
1802
|
this.ffmpegAvailable = true;
|
|
1879
1803
|
logger7.info("FFmpeg found at:", {
|
|
@@ -1901,9 +1825,7 @@ var TranscribeManager = class _TranscribeManager {
|
|
|
1901
1825
|
const { stdout } = await execAsync2("ffmpeg -codecs");
|
|
1902
1826
|
const hasRequiredCodecs = stdout.includes("pcm_s16le") && stdout.includes("wav");
|
|
1903
1827
|
if (!hasRequiredCodecs) {
|
|
1904
|
-
throw new Error(
|
|
1905
|
-
"FFmpeg installation missing required codecs (pcm_s16le, wav)"
|
|
1906
|
-
);
|
|
1828
|
+
throw new Error("FFmpeg installation missing required codecs (pcm_s16le, wav)");
|
|
1907
1829
|
}
|
|
1908
1830
|
} catch (error) {
|
|
1909
1831
|
logger7.error("FFmpeg capabilities verification failed:", {
|
|
@@ -1917,20 +1839,17 @@ var TranscribeManager = class _TranscribeManager {
|
|
|
1917
1839
|
* Logs instructions on how to install FFmpeg if it is not properly installed.
|
|
1918
1840
|
*/
|
|
1919
1841
|
logFFmpegInstallInstructions() {
|
|
1920
|
-
logger7.warn(
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1932
|
-
}
|
|
1933
|
-
);
|
|
1842
|
+
logger7.warn("FFmpeg is required but not properly installed. Please install FFmpeg:", {
|
|
1843
|
+
instructions: {
|
|
1844
|
+
mac: "brew install ffmpeg",
|
|
1845
|
+
ubuntu: "sudo apt-get install ffmpeg",
|
|
1846
|
+
windows: "choco install ffmpeg",
|
|
1847
|
+
manual: "Download from https://ffmpeg.org/download.html"
|
|
1848
|
+
},
|
|
1849
|
+
requiredVersion: "4.0 or later",
|
|
1850
|
+
requiredCodecs: ["pcm_s16le", "wav"],
|
|
1851
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1852
|
+
});
|
|
1934
1853
|
}
|
|
1935
1854
|
/**
|
|
1936
1855
|
* Gets the singleton instance of TranscribeManager, creates a new instance if it doesn't exist.
|
|
@@ -2008,15 +1927,10 @@ var TranscribeManager = class _TranscribeManager {
|
|
|
2008
1927
|
*/
|
|
2009
1928
|
async preprocessAudio(audioBuffer) {
|
|
2010
1929
|
if (!this.ffmpegAvailable) {
|
|
2011
|
-
throw new Error(
|
|
2012
|
-
"FFmpeg is not installed. Please install FFmpeg to use audio transcription."
|
|
2013
|
-
);
|
|
1930
|
+
throw new Error("FFmpeg is not installed. Please install FFmpeg to use audio transcription.");
|
|
2014
1931
|
}
|
|
2015
1932
|
try {
|
|
2016
|
-
const tempInputFile = path2.join(
|
|
2017
|
-
this.cacheDir,
|
|
2018
|
-
`temp_input_${Date.now()}`
|
|
2019
|
-
);
|
|
1933
|
+
const tempInputFile = path2.join(this.cacheDir, `temp_input_${Date.now()}`);
|
|
2020
1934
|
const tempWavFile = path2.join(this.cacheDir, `temp_${Date.now()}.wav`);
|
|
2021
1935
|
fs2.writeFileSync(tempInputFile, audioBuffer);
|
|
2022
1936
|
await this.convertToWav(tempInputFile, tempWavFile);
|
|
@@ -2126,12 +2040,7 @@ function getWavHeader(audioLength, sampleRate, channelCount = 1, bitsPerSample =
|
|
|
2126
2040
|
return wavHeader;
|
|
2127
2041
|
}
|
|
2128
2042
|
function prependWavHeader(readable, audioLength, sampleRate, channelCount = 1, bitsPerSample = 16) {
|
|
2129
|
-
const wavHeader = getWavHeader(
|
|
2130
|
-
audioLength,
|
|
2131
|
-
sampleRate,
|
|
2132
|
-
channelCount,
|
|
2133
|
-
bitsPerSample
|
|
2134
|
-
);
|
|
2043
|
+
const wavHeader = getWavHeader(audioLength, sampleRate, channelCount, bitsPerSample);
|
|
2135
2044
|
let pushedHeader = false;
|
|
2136
2045
|
const passThrough = new PassThrough();
|
|
2137
2046
|
readable.on("data", (data) => {
|
|
@@ -2165,10 +2074,7 @@ var TTSManager = class _TTSManager {
|
|
|
2165
2074
|
constructor(cacheDir) {
|
|
2166
2075
|
this.cacheDir = path3.join(cacheDir, "tts");
|
|
2167
2076
|
this.modelsDir = process.env.LLAMALOCAL_PATH?.trim() ? path3.resolve(process.env.LLAMALOCAL_PATH.trim()) : path3.join(process.cwd(), "models");
|
|
2168
|
-
this.downloadManager = DownloadManager.getInstance(
|
|
2169
|
-
this.cacheDir,
|
|
2170
|
-
this.modelsDir
|
|
2171
|
-
);
|
|
2077
|
+
this.downloadManager = DownloadManager.getInstance(this.cacheDir, this.modelsDir);
|
|
2172
2078
|
this.ensureCacheDirectory();
|
|
2173
2079
|
logger8.info("TTSManager initialized");
|
|
2174
2080
|
}
|
|
@@ -2244,10 +2150,7 @@ var TTSManager = class _TTSManager {
|
|
|
2244
2150
|
await this.downloadManager.downloadFromUrl(attempt.url, modelPath);
|
|
2245
2151
|
const completedBar = "\u25B0".repeat(barLength);
|
|
2246
2152
|
logger8.info(`Downloading TTS model: ${completedBar} 100%`);
|
|
2247
|
-
logger8.success(
|
|
2248
|
-
"TTS model download successful with:",
|
|
2249
|
-
attempt.description
|
|
2250
|
-
);
|
|
2153
|
+
logger8.success("TTS model download successful with:", attempt.description);
|
|
2251
2154
|
break;
|
|
2252
2155
|
} catch (error) {
|
|
2253
2156
|
lastError = error;
|
|
@@ -2319,9 +2222,7 @@ var TTSManager = class _TTSManager {
|
|
|
2319
2222
|
responseTokens.push(token);
|
|
2320
2223
|
const percent = Math.round(responseTokens.length / maxTokens * 100);
|
|
2321
2224
|
const barLength = 30;
|
|
2322
|
-
const filledLength = Math.floor(
|
|
2323
|
-
responseTokens.length / maxTokens * barLength
|
|
2324
|
-
);
|
|
2225
|
+
const filledLength = Math.floor(responseTokens.length / maxTokens * barLength);
|
|
2325
2226
|
const progressBar = "\u25B0".repeat(filledLength) + "\u25B1".repeat(barLength - filledLength);
|
|
2326
2227
|
logger8.info(
|
|
2327
2228
|
`Token generation: ${progressBar} ${percent}% (${responseTokens.length}/${maxTokens})`
|
|
@@ -2340,9 +2241,7 @@ var TTSManager = class _TTSManager {
|
|
|
2340
2241
|
}
|
|
2341
2242
|
logger8.info("Converting tokens to audio data...");
|
|
2342
2243
|
const audioData = this.processAudioResponse({
|
|
2343
|
-
tokens: responseTokens.map(
|
|
2344
|
-
(t) => Number.parseInt(this.model.detokenize([t]), 10)
|
|
2345
|
-
)
|
|
2244
|
+
tokens: responseTokens.map((t) => Number.parseInt(this.model.detokenize([t]), 10))
|
|
2346
2245
|
});
|
|
2347
2246
|
logger8.info("Audio data generated:", {
|
|
2348
2247
|
byteLength: audioData.length,
|
|
@@ -2436,10 +2335,7 @@ var VisionManager = class _VisionManager {
|
|
|
2436
2335
|
this.modelsDir = path4.join(path4.dirname(cacheDir), "models", "vision");
|
|
2437
2336
|
this.cacheDir = cacheDir;
|
|
2438
2337
|
this.ensureModelsDirExists();
|
|
2439
|
-
this.downloadManager = DownloadManager.getInstance(
|
|
2440
|
-
this.cacheDir,
|
|
2441
|
-
this.modelsDir
|
|
2442
|
-
);
|
|
2338
|
+
this.downloadManager = DownloadManager.getInstance(this.cacheDir, this.modelsDir);
|
|
2443
2339
|
this.platformConfig = this.getPlatformConfig();
|
|
2444
2340
|
logger9.info("VisionManager initialized");
|
|
2445
2341
|
}
|
|
@@ -2508,11 +2404,7 @@ var VisionManager = class _VisionManager {
|
|
|
2508
2404
|
* @returns {boolean} - Returns true if cache exists, otherwise returns false.
|
|
2509
2405
|
*/
|
|
2510
2406
|
checkCacheExists(modelId, type) {
|
|
2511
|
-
const modelPath = path4.join(
|
|
2512
|
-
this.modelsDir,
|
|
2513
|
-
modelId.replace("/", "--"),
|
|
2514
|
-
type
|
|
2515
|
-
);
|
|
2407
|
+
const modelPath = path4.join(this.modelsDir, modelId.replace("/", "--"), type);
|
|
2516
2408
|
if (existsSync(modelPath)) {
|
|
2517
2409
|
logger9.info(`${type} found at: ${modelPath}`);
|
|
2518
2410
|
return true;
|
|
@@ -2550,9 +2442,7 @@ var VisionManager = class _VisionManager {
|
|
|
2550
2442
|
* @returns {object} The model configuration object containing device, dtype, and cache_dir.
|
|
2551
2443
|
*/
|
|
2552
2444
|
getModelConfig(componentName) {
|
|
2553
|
-
const component = this.modelComponents.find(
|
|
2554
|
-
(c) => c.name === componentName
|
|
2555
|
-
);
|
|
2445
|
+
const component = this.modelComponents.find((c) => c.name === componentName);
|
|
2556
2446
|
return {
|
|
2557
2447
|
device: this.platformConfig.device,
|
|
2558
2448
|
dtype: component?.dtype || "fp32",
|
|
@@ -2568,9 +2458,7 @@ var VisionManager = class _VisionManager {
|
|
|
2568
2458
|
async initialize() {
|
|
2569
2459
|
try {
|
|
2570
2460
|
if (this.initialized) {
|
|
2571
|
-
logger9.info(
|
|
2572
|
-
"Vision model already initialized, skipping initialization"
|
|
2573
|
-
);
|
|
2461
|
+
logger9.info("Vision model already initialized, skipping initialization");
|
|
2574
2462
|
return;
|
|
2575
2463
|
}
|
|
2576
2464
|
logger9.info("Starting vision model initialization...");
|
|
@@ -2586,32 +2474,25 @@ var VisionManager = class _VisionManager {
|
|
|
2586
2474
|
try {
|
|
2587
2475
|
let lastProgress = -1;
|
|
2588
2476
|
const modelCached = this.checkCacheExists(modelSpec.modelId, "model");
|
|
2589
|
-
const model = await Florence2ForConditionalGeneration.from_pretrained(
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
);
|
|
2606
|
-
const progressBar = "\u25B0".repeat(filledLength) + "\u25B1".repeat(barLength - filledLength);
|
|
2607
|
-
logger9.info(
|
|
2608
|
-
`Downloading vision model: ${progressBar} ${currentProgress}%`
|
|
2609
|
-
);
|
|
2610
|
-
if (currentProgress === 100) this.modelDownloaded = true;
|
|
2611
|
-
}
|
|
2477
|
+
const model = await Florence2ForConditionalGeneration.from_pretrained(modelSpec.modelId, {
|
|
2478
|
+
device: "cpu",
|
|
2479
|
+
cache_dir: this.modelsDir,
|
|
2480
|
+
local_files_only: modelCached,
|
|
2481
|
+
revision: "main",
|
|
2482
|
+
progress_callback: (progressInfo) => {
|
|
2483
|
+
if (modelCached || this.modelDownloaded) return;
|
|
2484
|
+
const progress = "progress" in progressInfo ? Math.max(0, Math.min(1, progressInfo.progress)) : 0;
|
|
2485
|
+
const currentProgress = Math.round(progress * 100);
|
|
2486
|
+
if (currentProgress > lastProgress + 9 || currentProgress === 100) {
|
|
2487
|
+
lastProgress = currentProgress;
|
|
2488
|
+
const barLength = 30;
|
|
2489
|
+
const filledLength = Math.floor(currentProgress / 100 * barLength);
|
|
2490
|
+
const progressBar = "\u25B0".repeat(filledLength) + "\u25B1".repeat(barLength - filledLength);
|
|
2491
|
+
logger9.info(`Downloading vision model: ${progressBar} ${currentProgress}%`);
|
|
2492
|
+
if (currentProgress === 100) this.modelDownloaded = true;
|
|
2612
2493
|
}
|
|
2613
2494
|
}
|
|
2614
|
-
);
|
|
2495
|
+
});
|
|
2615
2496
|
this.model = model;
|
|
2616
2497
|
logger9.success("Florence2 model loaded successfully");
|
|
2617
2498
|
} catch (error) {
|
|
@@ -2624,35 +2505,25 @@ var VisionManager = class _VisionManager {
|
|
|
2624
2505
|
}
|
|
2625
2506
|
logger9.info("Loading vision tokenizer...");
|
|
2626
2507
|
try {
|
|
2627
|
-
const tokenizerCached = this.checkCacheExists(
|
|
2628
|
-
modelSpec.modelId,
|
|
2629
|
-
"tokenizer"
|
|
2630
|
-
);
|
|
2508
|
+
const tokenizerCached = this.checkCacheExists(modelSpec.modelId, "tokenizer");
|
|
2631
2509
|
let tokenizerProgress = -1;
|
|
2632
|
-
this.tokenizer = await AutoTokenizer2.from_pretrained(
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
);
|
|
2647
|
-
const progressBar = "\u25B0".repeat(filledLength) + "\u25B1".repeat(barLength - filledLength);
|
|
2648
|
-
logger9.info(
|
|
2649
|
-
`Downloading vision tokenizer: ${progressBar} ${currentProgress}%`
|
|
2650
|
-
);
|
|
2651
|
-
if (currentProgress === 100) this.tokenizerDownloaded = true;
|
|
2652
|
-
}
|
|
2510
|
+
this.tokenizer = await AutoTokenizer2.from_pretrained(modelSpec.modelId, {
|
|
2511
|
+
cache_dir: this.modelsDir,
|
|
2512
|
+
local_files_only: tokenizerCached,
|
|
2513
|
+
progress_callback: (progressInfo) => {
|
|
2514
|
+
if (tokenizerCached || this.tokenizerDownloaded) return;
|
|
2515
|
+
const progress = "progress" in progressInfo ? Math.max(0, Math.min(1, progressInfo.progress)) : 0;
|
|
2516
|
+
const currentProgress = Math.round(progress * 100);
|
|
2517
|
+
if (currentProgress !== tokenizerProgress) {
|
|
2518
|
+
tokenizerProgress = currentProgress;
|
|
2519
|
+
const barLength = 30;
|
|
2520
|
+
const filledLength = Math.floor(currentProgress / 100 * barLength);
|
|
2521
|
+
const progressBar = "\u25B0".repeat(filledLength) + "\u25B1".repeat(barLength - filledLength);
|
|
2522
|
+
logger9.info(`Downloading vision tokenizer: ${progressBar} ${currentProgress}%`);
|
|
2523
|
+
if (currentProgress === 100) this.tokenizerDownloaded = true;
|
|
2653
2524
|
}
|
|
2654
2525
|
}
|
|
2655
|
-
);
|
|
2526
|
+
});
|
|
2656
2527
|
logger9.success("Vision tokenizer loaded successfully");
|
|
2657
2528
|
} catch (error) {
|
|
2658
2529
|
logger9.error("Failed to load tokenizer:", {
|
|
@@ -2664,36 +2535,26 @@ var VisionManager = class _VisionManager {
|
|
|
2664
2535
|
}
|
|
2665
2536
|
logger9.info("Loading vision processor...");
|
|
2666
2537
|
try {
|
|
2667
|
-
const processorCached = this.checkCacheExists(
|
|
2668
|
-
modelSpec.modelId,
|
|
2669
|
-
"processor"
|
|
2670
|
-
);
|
|
2538
|
+
const processorCached = this.checkCacheExists(modelSpec.modelId, "processor");
|
|
2671
2539
|
let processorProgress = -1;
|
|
2672
|
-
this.processor = await AutoProcessor.from_pretrained(
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
);
|
|
2688
|
-
const progressBar = "\u25B0".repeat(filledLength) + "\u25B1".repeat(barLength - filledLength);
|
|
2689
|
-
logger9.info(
|
|
2690
|
-
`Downloading vision processor: ${progressBar} ${currentProgress}%`
|
|
2691
|
-
);
|
|
2692
|
-
if (currentProgress === 100) this.processorDownloaded = true;
|
|
2693
|
-
}
|
|
2540
|
+
this.processor = await AutoProcessor.from_pretrained(modelSpec.modelId, {
|
|
2541
|
+
device: "cpu",
|
|
2542
|
+
cache_dir: this.modelsDir,
|
|
2543
|
+
local_files_only: processorCached,
|
|
2544
|
+
progress_callback: (progressInfo) => {
|
|
2545
|
+
if (processorCached || this.processorDownloaded) return;
|
|
2546
|
+
const progress = "progress" in progressInfo ? Math.max(0, Math.min(1, progressInfo.progress)) : 0;
|
|
2547
|
+
const currentProgress = Math.round(progress * 100);
|
|
2548
|
+
if (currentProgress !== processorProgress) {
|
|
2549
|
+
processorProgress = currentProgress;
|
|
2550
|
+
const barLength = 30;
|
|
2551
|
+
const filledLength = Math.floor(currentProgress / 100 * barLength);
|
|
2552
|
+
const progressBar = "\u25B0".repeat(filledLength) + "\u25B1".repeat(barLength - filledLength);
|
|
2553
|
+
logger9.info(`Downloading vision processor: ${progressBar} ${currentProgress}%`);
|
|
2554
|
+
if (currentProgress === 100) this.processorDownloaded = true;
|
|
2694
2555
|
}
|
|
2695
2556
|
}
|
|
2696
|
-
);
|
|
2557
|
+
});
|
|
2697
2558
|
logger9.success("Vision processor loaded successfully");
|
|
2698
2559
|
} catch (error) {
|
|
2699
2560
|
logger9.error("Failed to load vision processor:", {
|
|
@@ -2927,14 +2788,8 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
2927
2788
|
}
|
|
2928
2789
|
this.modelsDir = modelsDir;
|
|
2929
2790
|
}
|
|
2930
|
-
this.modelPath = path5.join(
|
|
2931
|
-
|
|
2932
|
-
"DeepSeek-R1-Distill-Qwen-1.5B-Q8_0.gguf"
|
|
2933
|
-
);
|
|
2934
|
-
this.mediumModelPath = path5.join(
|
|
2935
|
-
this.modelsDir,
|
|
2936
|
-
"DeepSeek-R1-Distill-Qwen-7B-Q5_K_M.gguf"
|
|
2937
|
-
);
|
|
2791
|
+
this.modelPath = path5.join(this.modelsDir, "DeepHermes-3-Llama-3-3B-Preview-q4.gguf");
|
|
2792
|
+
this.mediumModelPath = path5.join(this.modelsDir, "DeepHermes-3-Llama-3-8B-q4.gguf");
|
|
2938
2793
|
const cacheDirEnv = process.env.CACHE_DIR?.trim();
|
|
2939
2794
|
if (cacheDirEnv) {
|
|
2940
2795
|
this.cacheDir = path5.resolve(cacheDirEnv);
|
|
@@ -2985,12 +2840,8 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
2985
2840
|
const validatedConfig = await validateConfig(config);
|
|
2986
2841
|
logger10.info("Environment configuration validated");
|
|
2987
2842
|
process.env.USE_LOCAL_AI = String(validatedConfig.USE_LOCAL_AI);
|
|
2988
|
-
process.env.USE_STUDIOLM_TEXT_MODELS = String(
|
|
2989
|
-
|
|
2990
|
-
);
|
|
2991
|
-
process.env.USE_OLLAMA_TEXT_MODELS = String(
|
|
2992
|
-
validatedConfig.USE_OLLAMA_TEXT_MODELS
|
|
2993
|
-
);
|
|
2843
|
+
process.env.USE_STUDIOLM_TEXT_MODELS = String(validatedConfig.USE_STUDIOLM_TEXT_MODELS);
|
|
2844
|
+
process.env.USE_OLLAMA_TEXT_MODELS = String(validatedConfig.USE_OLLAMA_TEXT_MODELS);
|
|
2994
2845
|
logger10.success("Environment initialization complete");
|
|
2995
2846
|
} catch (error) {
|
|
2996
2847
|
logger10.error("Environment validation failed:", {
|
|
@@ -3014,9 +2865,7 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
3014
2865
|
}
|
|
3015
2866
|
await this.ollamaManager.initialize();
|
|
3016
2867
|
if (!this.ollamaManager.isInitialized()) {
|
|
3017
|
-
throw new Error(
|
|
3018
|
-
"Ollama initialization failed - models not properly loaded"
|
|
3019
|
-
);
|
|
2868
|
+
throw new Error("Ollama initialization failed - models not properly loaded");
|
|
3020
2869
|
}
|
|
3021
2870
|
logger10.success("Ollama initialization complete");
|
|
3022
2871
|
} catch (error) {
|
|
@@ -3041,9 +2890,7 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
3041
2890
|
}
|
|
3042
2891
|
await this.studioLMManager.initialize();
|
|
3043
2892
|
if (!this.studioLMManager.isInitialized()) {
|
|
3044
|
-
throw new Error(
|
|
3045
|
-
"StudioLM initialization failed - models not properly loaded"
|
|
3046
|
-
);
|
|
2893
|
+
throw new Error("StudioLM initialization failed - models not properly loaded");
|
|
3047
2894
|
}
|
|
3048
2895
|
this.studioLMInitialized = true;
|
|
3049
2896
|
logger10.success("StudioLM initialization complete");
|
|
@@ -3062,18 +2909,15 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
3062
2909
|
*
|
|
3063
2910
|
* @returns A Promise that resolves to a boolean indicating whether the model download was successful.
|
|
3064
2911
|
*/
|
|
3065
|
-
async downloadModel() {
|
|
2912
|
+
async downloadModel(modelType) {
|
|
2913
|
+
const modelSpec = modelType === ModelType3.TEXT_LARGE ? MODEL_SPECS.medium : MODEL_SPECS.small;
|
|
2914
|
+
const modelPath = modelType === ModelType3.TEXT_LARGE ? this.mediumModelPath : this.modelPath;
|
|
3066
2915
|
try {
|
|
3067
|
-
|
|
3068
|
-
const modelSpec = isLargeModel ? MODEL_SPECS.medium : MODEL_SPECS.small;
|
|
3069
|
-
return await this.downloadManager.downloadModel(
|
|
3070
|
-
modelSpec,
|
|
3071
|
-
this.modelPath
|
|
3072
|
-
);
|
|
2916
|
+
return await this.downloadManager.downloadModel(modelSpec, modelPath);
|
|
3073
2917
|
} catch (error) {
|
|
3074
2918
|
logger10.error("Model download failed:", {
|
|
3075
2919
|
error: error instanceof Error ? error.message : String(error),
|
|
3076
|
-
modelPath
|
|
2920
|
+
modelPath
|
|
3077
2921
|
});
|
|
3078
2922
|
throw error;
|
|
3079
2923
|
}
|
|
@@ -3121,16 +2965,11 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
3121
2965
|
logger10.info("Initializing embedding model...");
|
|
3122
2966
|
logger10.info("Models directory:", this.modelsDir);
|
|
3123
2967
|
if (!fs5.existsSync(this.modelsDir)) {
|
|
3124
|
-
logger10.warn(
|
|
3125
|
-
"Models directory does not exist, creating it:",
|
|
3126
|
-
this.modelsDir
|
|
3127
|
-
);
|
|
2968
|
+
logger10.warn("Models directory does not exist, creating it:", this.modelsDir);
|
|
3128
2969
|
fs5.mkdirSync(this.modelsDir, { recursive: true });
|
|
3129
2970
|
}
|
|
3130
2971
|
if (!this.embeddingModel) {
|
|
3131
|
-
logger10.info(
|
|
3132
|
-
"Creating new FlagEmbedding instance with BGESmallENV15 model"
|
|
3133
|
-
);
|
|
2972
|
+
logger10.info("Creating new FlagEmbedding instance with BGESmallENV15 model");
|
|
3134
2973
|
const barLength = 30;
|
|
3135
2974
|
const emptyBar = "\u25B1".repeat(barLength);
|
|
3136
2975
|
logger10.info(`Downloading embedding model: ${emptyBar} 0%`);
|
|
@@ -3179,31 +3018,22 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
3179
3018
|
return this.generateText(params);
|
|
3180
3019
|
}
|
|
3181
3020
|
if (!this.studioLMManager) {
|
|
3182
|
-
logger10.warn(
|
|
3183
|
-
"StudioLM manager not initialized, falling back to local models"
|
|
3184
|
-
);
|
|
3021
|
+
logger10.warn("StudioLM manager not initialized, falling back to local models");
|
|
3185
3022
|
return this.generateText(params);
|
|
3186
3023
|
}
|
|
3187
3024
|
if (!this.studioLMInitialized) {
|
|
3188
3025
|
logger10.info("StudioLM not initialized, initializing now...");
|
|
3189
3026
|
await this.initializeStudioLM();
|
|
3190
3027
|
}
|
|
3191
|
-
return await this.studioLMManager.generateText(
|
|
3192
|
-
params,
|
|
3193
|
-
this.studioLMInitialized
|
|
3194
|
-
);
|
|
3028
|
+
return await this.studioLMManager.generateText(params, this.studioLMInitialized);
|
|
3195
3029
|
}
|
|
3196
3030
|
if (modelConfig.source === "ollama") {
|
|
3197
3031
|
if (process.env.USE_OLLAMA_TEXT_MODELS !== "true") {
|
|
3198
|
-
logger10.warn(
|
|
3199
|
-
"Ollama requested but disabled in environment, falling back to local models"
|
|
3200
|
-
);
|
|
3032
|
+
logger10.warn("Ollama requested but disabled in environment, falling back to local models");
|
|
3201
3033
|
return this.generateText(params);
|
|
3202
3034
|
}
|
|
3203
3035
|
if (!this.ollamaManager) {
|
|
3204
|
-
logger10.warn(
|
|
3205
|
-
"Ollama manager not initialized, falling back to local models"
|
|
3206
|
-
);
|
|
3036
|
+
logger10.warn("Ollama manager not initialized, falling back to local models");
|
|
3207
3037
|
return this.generateText(params);
|
|
3208
3038
|
}
|
|
3209
3039
|
if (!this.ollamaInitialized && !this.ollamaManager.isInitialized()) {
|
|
@@ -3211,10 +3041,7 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
3211
3041
|
await this.ollamaManager.initialize();
|
|
3212
3042
|
this.ollamaInitialized = true;
|
|
3213
3043
|
}
|
|
3214
|
-
return await this.ollamaManager.generateText(
|
|
3215
|
-
params,
|
|
3216
|
-
this.ollamaInitialized
|
|
3217
|
-
);
|
|
3044
|
+
return await this.ollamaManager.generateText(params, this.ollamaInitialized);
|
|
3218
3045
|
}
|
|
3219
3046
|
return this.generateText(params);
|
|
3220
3047
|
} catch (error) {
|
|
@@ -3270,10 +3097,7 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
3270
3097
|
runtime: !!params.runtime,
|
|
3271
3098
|
stopSequences: params.stopSequences
|
|
3272
3099
|
});
|
|
3273
|
-
const tokens = await this.tokenizerManager.encode(
|
|
3274
|
-
params.prompt,
|
|
3275
|
-
this.activeModelConfig
|
|
3276
|
-
);
|
|
3100
|
+
const tokens = await this.tokenizerManager.encode(params.prompt, this.activeModelConfig);
|
|
3277
3101
|
logger10.info("Input tokens:", { count: tokens.length });
|
|
3278
3102
|
const systemMessage = "You are a helpful AI assistant. Respond to the current request only.";
|
|
3279
3103
|
await this.chatSession.prompt(systemMessage, {
|
|
@@ -3439,7 +3263,7 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
3439
3263
|
this.smallModelInitializingPromise = (async () => {
|
|
3440
3264
|
await this.initializeEnvironment();
|
|
3441
3265
|
await this.checkPlatformCapabilities();
|
|
3442
|
-
await this.downloadModel();
|
|
3266
|
+
await this.downloadModel(ModelType3.TEXT_SMALL);
|
|
3443
3267
|
try {
|
|
3444
3268
|
this.llama = await getLlama2();
|
|
3445
3269
|
const smallModel = await this.llama.loadModel({
|
|
@@ -3474,14 +3298,13 @@ var LocalAIManager = class _LocalAIManager {
|
|
|
3474
3298
|
if (!this.llama) {
|
|
3475
3299
|
await this.lazyInitSmallModel();
|
|
3476
3300
|
}
|
|
3301
|
+
await this.downloadModel(ModelType3.TEXT_LARGE);
|
|
3477
3302
|
try {
|
|
3478
|
-
const mediumModel = await this.llama.loadModel(
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
}
|
|
3484
|
-
);
|
|
3303
|
+
const mediumModel = await this.llama.loadModel({
|
|
3304
|
+
gpuLayers: 43,
|
|
3305
|
+
modelPath: this.mediumModelPath,
|
|
3306
|
+
vocabOnly: false
|
|
3307
|
+
});
|
|
3485
3308
|
this.mediumModel = mediumModel;
|
|
3486
3309
|
this.mediumModelInitialized = true;
|
|
3487
3310
|
logger10.info("Medium model initialized successfully");
|
|
@@ -3674,7 +3497,6 @@ var localAIPlugin = {
|
|
|
3674
3497
|
}
|
|
3675
3498
|
},
|
|
3676
3499
|
[ModelType3.TEXT_EMBEDDING]: async (_runtime, params) => {
|
|
3677
|
-
console.log("params is", params)
|
|
3678
3500
|
const text = params?.text;
|
|
3679
3501
|
try {
|
|
3680
3502
|
logger10.info("TEXT_EMBEDDING handler - Initial input:", {
|
|
@@ -3708,6 +3530,168 @@ var localAIPlugin = {
|
|
|
3708
3530
|
return new Array(384).fill(0);
|
|
3709
3531
|
}
|
|
3710
3532
|
},
|
|
3533
|
+
[ModelType3.OBJECT_SMALL]: async (runtime, params) => {
|
|
3534
|
+
try {
|
|
3535
|
+
logger10.info("OBJECT_SMALL handler - Processing request:", {
|
|
3536
|
+
prompt: params.prompt,
|
|
3537
|
+
hasSchema: !!params.schema,
|
|
3538
|
+
temperature: params.temperature
|
|
3539
|
+
});
|
|
3540
|
+
let jsonPrompt = params.prompt;
|
|
3541
|
+
if (!jsonPrompt.includes("```json") && !jsonPrompt.includes("respond with valid JSON")) {
|
|
3542
|
+
jsonPrompt += "\nPlease respond with valid JSON only, without any explanations, markdown formatting, or additional text.";
|
|
3543
|
+
}
|
|
3544
|
+
const modelConfig = localAIManager.getTextModelSource();
|
|
3545
|
+
let textResponse;
|
|
3546
|
+
if (modelConfig.source !== "local") {
|
|
3547
|
+
textResponse = await localAIManager.generateTextOllamaStudio({
|
|
3548
|
+
prompt: jsonPrompt,
|
|
3549
|
+
stopSequences: params.stopSequences,
|
|
3550
|
+
runtime,
|
|
3551
|
+
modelType: ModelType3.TEXT_SMALL
|
|
3552
|
+
});
|
|
3553
|
+
} else {
|
|
3554
|
+
textResponse = await localAIManager.generateText({
|
|
3555
|
+
prompt: jsonPrompt,
|
|
3556
|
+
stopSequences: params.stopSequences,
|
|
3557
|
+
runtime,
|
|
3558
|
+
modelType: ModelType3.TEXT_SMALL
|
|
3559
|
+
});
|
|
3560
|
+
}
|
|
3561
|
+
try {
|
|
3562
|
+
const extractJSON = (text) => {
|
|
3563
|
+
const jsonBlockRegex = /```(?:json)?\s*([\s\S]*?)\s*```/;
|
|
3564
|
+
const match = text.match(jsonBlockRegex);
|
|
3565
|
+
if (match && match[1]) {
|
|
3566
|
+
return match[1].trim();
|
|
3567
|
+
}
|
|
3568
|
+
const jsonContentRegex = /\s*(\{[\s\S]*\})\s*$/;
|
|
3569
|
+
const contentMatch = text.match(jsonContentRegex);
|
|
3570
|
+
if (contentMatch && contentMatch[1]) {
|
|
3571
|
+
return contentMatch[1].trim();
|
|
3572
|
+
}
|
|
3573
|
+
return text.trim();
|
|
3574
|
+
};
|
|
3575
|
+
const extractedJsonText = extractJSON(textResponse);
|
|
3576
|
+
logger10.debug("Extracted JSON text:", extractedJsonText);
|
|
3577
|
+
let jsonObject;
|
|
3578
|
+
try {
|
|
3579
|
+
jsonObject = JSON.parse(extractedJsonText);
|
|
3580
|
+
} catch (parseError) {
|
|
3581
|
+
logger10.debug("Initial JSON parse failed, attempting to fix common issues");
|
|
3582
|
+
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");
|
|
3583
|
+
try {
|
|
3584
|
+
jsonObject = JSON.parse(fixedJson);
|
|
3585
|
+
} catch (finalError) {
|
|
3586
|
+
logger10.error("Failed to parse JSON after fixing:", finalError);
|
|
3587
|
+
throw new Error("Invalid JSON returned from model");
|
|
3588
|
+
}
|
|
3589
|
+
}
|
|
3590
|
+
if (params.schema) {
|
|
3591
|
+
try {
|
|
3592
|
+
for (const key of Object.keys(params.schema)) {
|
|
3593
|
+
if (!(key in jsonObject)) {
|
|
3594
|
+
jsonObject[key] = null;
|
|
3595
|
+
}
|
|
3596
|
+
}
|
|
3597
|
+
} catch (schemaError) {
|
|
3598
|
+
logger10.error("Schema validation failed:", schemaError);
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
return jsonObject;
|
|
3602
|
+
} catch (parseError) {
|
|
3603
|
+
logger10.error("Failed to parse JSON:", parseError);
|
|
3604
|
+
logger10.error("Raw response:", textResponse);
|
|
3605
|
+
throw new Error("Invalid JSON returned from model");
|
|
3606
|
+
}
|
|
3607
|
+
} catch (error) {
|
|
3608
|
+
logger10.error("Error in OBJECT_SMALL handler:", error);
|
|
3609
|
+
throw error;
|
|
3610
|
+
}
|
|
3611
|
+
},
|
|
3612
|
+
[ModelType3.OBJECT_LARGE]: async (runtime, params) => {
|
|
3613
|
+
try {
|
|
3614
|
+
logger10.info("OBJECT_LARGE handler - Processing request:", {
|
|
3615
|
+
prompt: params.prompt,
|
|
3616
|
+
hasSchema: !!params.schema,
|
|
3617
|
+
temperature: params.temperature
|
|
3618
|
+
});
|
|
3619
|
+
let jsonPrompt = params.prompt;
|
|
3620
|
+
if (!jsonPrompt.includes("```json") && !jsonPrompt.includes("respond with valid JSON")) {
|
|
3621
|
+
jsonPrompt += "\nPlease respond with valid JSON only, without any explanations, markdown formatting, or additional text.";
|
|
3622
|
+
}
|
|
3623
|
+
const modelConfig = localAIManager.getTextModelSource();
|
|
3624
|
+
let textResponse;
|
|
3625
|
+
if (modelConfig.source !== "local") {
|
|
3626
|
+
textResponse = await localAIManager.generateTextOllamaStudio({
|
|
3627
|
+
prompt: jsonPrompt,
|
|
3628
|
+
stopSequences: params.stopSequences,
|
|
3629
|
+
runtime,
|
|
3630
|
+
modelType: ModelType3.TEXT_LARGE
|
|
3631
|
+
});
|
|
3632
|
+
} else {
|
|
3633
|
+
textResponse = await localAIManager.generateText({
|
|
3634
|
+
prompt: jsonPrompt,
|
|
3635
|
+
stopSequences: params.stopSequences,
|
|
3636
|
+
runtime,
|
|
3637
|
+
modelType: ModelType3.TEXT_LARGE
|
|
3638
|
+
});
|
|
3639
|
+
}
|
|
3640
|
+
try {
|
|
3641
|
+
const extractJSON = (text) => {
|
|
3642
|
+
const jsonBlockRegex = /```(?:json)?\s*([\s\S]*?)\s*```/;
|
|
3643
|
+
const match = text.match(jsonBlockRegex);
|
|
3644
|
+
if (match && match[1]) {
|
|
3645
|
+
return match[1].trim();
|
|
3646
|
+
}
|
|
3647
|
+
const jsonContentRegex = /\s*(\{[\s\S]*\})\s*$/;
|
|
3648
|
+
const contentMatch = text.match(jsonContentRegex);
|
|
3649
|
+
if (contentMatch && contentMatch[1]) {
|
|
3650
|
+
return contentMatch[1].trim();
|
|
3651
|
+
}
|
|
3652
|
+
return text.trim();
|
|
3653
|
+
};
|
|
3654
|
+
const cleanupJSON = (jsonText) => {
|
|
3655
|
+
return jsonText.replace(/\[DEBUG\].*?(\n|$)/g, "\n").replace(/\[LOG\].*?(\n|$)/g, "\n").replace(/console\.log.*?(\n|$)/g, "\n");
|
|
3656
|
+
};
|
|
3657
|
+
const extractedJsonText = extractJSON(textResponse);
|
|
3658
|
+
const cleanedJsonText = cleanupJSON(extractedJsonText);
|
|
3659
|
+
logger10.debug("Extracted JSON text:", cleanedJsonText);
|
|
3660
|
+
let jsonObject;
|
|
3661
|
+
try {
|
|
3662
|
+
jsonObject = JSON.parse(cleanedJsonText);
|
|
3663
|
+
} catch (parseError) {
|
|
3664
|
+
logger10.debug("Initial JSON parse failed, attempting to fix common issues");
|
|
3665
|
+
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");
|
|
3666
|
+
try {
|
|
3667
|
+
jsonObject = JSON.parse(fixedJson);
|
|
3668
|
+
} catch (finalError) {
|
|
3669
|
+
logger10.error("Failed to parse JSON after fixing:", finalError);
|
|
3670
|
+
throw new Error("Invalid JSON returned from model");
|
|
3671
|
+
}
|
|
3672
|
+
}
|
|
3673
|
+
if (params.schema) {
|
|
3674
|
+
try {
|
|
3675
|
+
for (const key of Object.keys(params.schema)) {
|
|
3676
|
+
if (!(key in jsonObject)) {
|
|
3677
|
+
jsonObject[key] = null;
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3680
|
+
} catch (schemaError) {
|
|
3681
|
+
logger10.error("Schema validation failed:", schemaError);
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
return jsonObject;
|
|
3685
|
+
} catch (parseError) {
|
|
3686
|
+
logger10.error("Failed to parse JSON:", parseError);
|
|
3687
|
+
logger10.error("Raw response:", textResponse);
|
|
3688
|
+
throw new Error("Invalid JSON returned from model");
|
|
3689
|
+
}
|
|
3690
|
+
} catch (error) {
|
|
3691
|
+
logger10.error("Error in OBJECT_LARGE handler:", error);
|
|
3692
|
+
throw error;
|
|
3693
|
+
}
|
|
3694
|
+
},
|
|
3711
3695
|
[ModelType3.TEXT_TOKENIZER_ENCODE]: async (_runtime, { text }) => {
|
|
3712
3696
|
try {
|
|
3713
3697
|
const manager = localAIManager.getTokenizerManager();
|
|
@@ -3833,16 +3817,10 @@ var localAIPlugin = {
|
|
|
3833
3817
|
fn: async (runtime) => {
|
|
3834
3818
|
try {
|
|
3835
3819
|
logger10.info("Starting TEXT_EMBEDDING test");
|
|
3836
|
-
const embedding = await runtime.useModel(
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
}
|
|
3841
|
-
);
|
|
3842
|
-
logger10.info(
|
|
3843
|
-
"Embedding generated with dimensions:",
|
|
3844
|
-
embedding.length
|
|
3845
|
-
);
|
|
3820
|
+
const embedding = await runtime.useModel(ModelType3.TEXT_EMBEDDING, {
|
|
3821
|
+
text: "This is a test of the text embedding model."
|
|
3822
|
+
});
|
|
3823
|
+
logger10.info("Embedding generated with dimensions:", embedding.length);
|
|
3846
3824
|
if (!Array.isArray(embedding)) {
|
|
3847
3825
|
throw new Error("Embedding is not an array");
|
|
3848
3826
|
}
|
|
@@ -3852,10 +3830,7 @@ var localAIPlugin = {
|
|
|
3852
3830
|
if (embedding.some((val) => typeof val !== "number")) {
|
|
3853
3831
|
throw new Error("Embedding contains non-numeric values");
|
|
3854
3832
|
}
|
|
3855
|
-
const nullEmbedding = await runtime.useModel(
|
|
3856
|
-
ModelType3.TEXT_EMBEDDING,
|
|
3857
|
-
null
|
|
3858
|
-
);
|
|
3833
|
+
const nullEmbedding = await runtime.useModel(ModelType3.TEXT_EMBEDDING, null);
|
|
3859
3834
|
if (!Array.isArray(nullEmbedding) || nullEmbedding.some((val) => val !== 0)) {
|
|
3860
3835
|
throw new Error("Null input did not return zero vector");
|
|
3861
3836
|
}
|
|
@@ -3875,10 +3850,7 @@ var localAIPlugin = {
|
|
|
3875
3850
|
try {
|
|
3876
3851
|
logger10.info("Starting TEXT_TOKENIZER_ENCODE test");
|
|
3877
3852
|
const text = "Hello tokenizer test!";
|
|
3878
|
-
const tokens = await runtime.useModel(
|
|
3879
|
-
ModelType3.TEXT_TOKENIZER_ENCODE,
|
|
3880
|
-
{ text }
|
|
3881
|
-
);
|
|
3853
|
+
const tokens = await runtime.useModel(ModelType3.TEXT_TOKENIZER_ENCODE, { text });
|
|
3882
3854
|
logger10.info("Encoded tokens:", { count: tokens.length });
|
|
3883
3855
|
if (!Array.isArray(tokens)) {
|
|
3884
3856
|
throw new Error("Tokens output is not an array");
|
|
@@ -3889,9 +3861,7 @@ var localAIPlugin = {
|
|
|
3889
3861
|
if (tokens.some((token) => !Number.isInteger(token))) {
|
|
3890
3862
|
throw new Error("Tokens contain non-integer values");
|
|
3891
3863
|
}
|
|
3892
|
-
logger10.success(
|
|
3893
|
-
"TEXT_TOKENIZER_ENCODE test completed successfully"
|
|
3894
|
-
);
|
|
3864
|
+
logger10.success("TEXT_TOKENIZER_ENCODE test completed successfully");
|
|
3895
3865
|
} catch (error) {
|
|
3896
3866
|
logger10.error("TEXT_TOKENIZER_ENCODE test failed:", {
|
|
3897
3867
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -3907,14 +3877,12 @@ var localAIPlugin = {
|
|
|
3907
3877
|
try {
|
|
3908
3878
|
logger10.info("Starting TEXT_TOKENIZER_DECODE test");
|
|
3909
3879
|
const originalText = "Hello tokenizer test!";
|
|
3910
|
-
const tokens = await runtime.useModel(
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
{ tokens }
|
|
3917
|
-
);
|
|
3880
|
+
const tokens = await runtime.useModel(ModelType3.TEXT_TOKENIZER_ENCODE, {
|
|
3881
|
+
text: originalText
|
|
3882
|
+
});
|
|
3883
|
+
const decodedText = await runtime.useModel(ModelType3.TEXT_TOKENIZER_DECODE, {
|
|
3884
|
+
tokens
|
|
3885
|
+
});
|
|
3918
3886
|
logger10.info("Round trip tokenization:", {
|
|
3919
3887
|
original: originalText,
|
|
3920
3888
|
decoded: decodedText
|
|
@@ -3922,9 +3890,7 @@ var localAIPlugin = {
|
|
|
3922
3890
|
if (typeof decodedText !== "string") {
|
|
3923
3891
|
throw new Error("Decoded output is not a string");
|
|
3924
3892
|
}
|
|
3925
|
-
logger10.success(
|
|
3926
|
-
"TEXT_TOKENIZER_DECODE test completed successfully"
|
|
3927
|
-
);
|
|
3893
|
+
logger10.success("TEXT_TOKENIZER_DECODE test completed successfully");
|
|
3928
3894
|
} catch (error) {
|
|
3929
3895
|
logger10.error("TEXT_TOKENIZER_DECODE test failed:", {
|
|
3930
3896
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -3940,10 +3906,7 @@ var localAIPlugin = {
|
|
|
3940
3906
|
try {
|
|
3941
3907
|
logger10.info("Starting IMAGE_DESCRIPTION test");
|
|
3942
3908
|
const imageUrl = "https://raw.githubusercontent.com/microsoft/FLAML/main/website/static/img/flaml.png";
|
|
3943
|
-
const result = await runtime.useModel(
|
|
3944
|
-
ModelType3.IMAGE_DESCRIPTION,
|
|
3945
|
-
imageUrl
|
|
3946
|
-
);
|
|
3909
|
+
const result = await runtime.useModel(ModelType3.IMAGE_DESCRIPTION, imageUrl);
|
|
3947
3910
|
logger10.info("Image description result:", result);
|
|
3948
3911
|
if (!result || typeof result !== "object") {
|
|
3949
3912
|
throw new Error("Invalid response format");
|
|
@@ -3992,10 +3955,7 @@ var localAIPlugin = {
|
|
|
3992
3955
|
// "fmt "
|
|
3993
3956
|
]);
|
|
3994
3957
|
const audioBuffer = Buffer.from(audioData);
|
|
3995
|
-
const transcription = await runtime.useModel(
|
|
3996
|
-
ModelType3.TRANSCRIPTION,
|
|
3997
|
-
audioBuffer
|
|
3998
|
-
);
|
|
3958
|
+
const transcription = await runtime.useModel(ModelType3.TRANSCRIPTION, audioBuffer);
|
|
3999
3959
|
logger10.info("Transcription result:", transcription);
|
|
4000
3960
|
if (typeof transcription !== "string") {
|
|
4001
3961
|
throw new Error("Transcription result is not a string");
|
|
@@ -4016,10 +3976,7 @@ var localAIPlugin = {
|
|
|
4016
3976
|
try {
|
|
4017
3977
|
logger10.info("Starting TEXT_TO_SPEECH test");
|
|
4018
3978
|
const testText = "This is a test of the text to speech system.";
|
|
4019
|
-
const audioStream = await runtime.useModel(
|
|
4020
|
-
ModelType3.TEXT_TO_SPEECH,
|
|
4021
|
-
testText
|
|
4022
|
-
);
|
|
3979
|
+
const audioStream = await runtime.useModel(ModelType3.TEXT_TO_SPEECH, testText);
|
|
4023
3980
|
if (!(audioStream instanceof Readable2)) {
|
|
4024
3981
|
throw new Error("TTS output is not a readable stream");
|
|
4025
3982
|
}
|